游戏开发论坛

 找回密码
 立即注册
搜索
查看: 13238|回复: 41

跪求高手指导sse

[复制链接]

32

主题

377

帖子

378

积分

中级会员

Rank: 3Rank: 3

积分
378
发表于 2004-4-28 19:29:00 | 显示全部楼层 |阅读模式
本人想在dx中用sse写一高效copy图片的函数(是在vc++6中用内嵌汇编,我曾用mmx写了一个但觉得还不够快).无奈本人能力有限,老师又不教,故杀上bbs望高人指导如何使用.万分感谢!!(我试过一次,但sse指令通不过编译) [em13]

32

主题

377

帖子

378

积分

中级会员

Rank: 3Rank: 3

积分
378
 楼主| 发表于 2004-4-28 21:09:00 | 显示全部楼层

Re: 跪求高手指导sse

请告诉我相关指令,我用了一次连编译都通不过!谢谢.

6

主题

444

帖子

457

积分

中级会员

Rank: 3Rank: 3

积分
457
发表于 2004-4-28 21:16:00 | 显示全部楼层

Re:跪求高手指导sse

我的建议:下个ICC,然后用它优化编译一下,就ok了。效果绝对不差。(看看ICC的文档,它里面讲了如何编写代码有助于编译器优化。)
如果你非想自己写,那也可以,基本思路很简单估计你也都清楚,无非就是分块-〉lop1:prefetch-〉lop2:连续movq-〉连续movntq-〉循环lop2-〉循环lop1-〉处理分块的剩余部分。通不过编译可能是VC的问题(VC6直接支持SSE指令么)反正VC.net2003是没问题的。
指令么?无非是PREFETCHn,MOVQ,MOVNTQ罢了。其余的都是一般的汇编指令。

30

主题

298

帖子

299

积分

中级会员

Rank: 3Rank: 3

积分
299
QQ
发表于 2004-4-28 22:27:00 | 显示全部楼层

Re:跪求高手指导sse

这是我最近用SSE写的内存拷贝函数,在DDR266,外频133的机器上,大约能达到1.6-1.85G的带宽,注释也写得比较清楚了,希望能对你有用。

/*                SSE
void _memcpy(void* dst,const void* src,unsigned long nSize);
说明:复制指定的内存到指写的内存
参数:        void* dst                目标内存指针,4字节对齐
                const void* src        源内存的指针,4字节对齐
                unsigned long        欲填充的长度,单位为字节,必须为DWORD的整数倍,即能被4整除,大于等于4字节
返回值:无
*/
void _memcpy(void* dst,const void* src,unsigned long nSize){
        _asm{
                mov edi,dst
                mov esi,src
                mov edx,nSize
;前置检查,检查源地址与目标地址的对齐情况,以便分别处理
                /*test esi,07h                ;检查源地址是否8字节对齐
                jnz chkalign1

                test edi,07h                ;检查目标地址是否8字节对齐
                jnz        copy2
                jmp copy1
chkalign1:
                test edi,07h                ;检查目标地址是否8字节对齐
                jnz copy4
                jmp copy3*/

;为四种不同的情况做单独的处理

//copy1:                        ;对齐读对齐写
                mov ecx,edi                        ;取得目标地址
                and ecx,0FFFFFFE0h        ;取得上一个32字节对齐的地址
                jz premain                        ;对齐则跳过头块处理

                add ecx,20h                        ;计算出下一个32字节对齐的地址

                sub ecx,edi                        ;计算出到下一个32字节对齐的地址之间的字节数
                and ecx,1Fh                        ;取低5位

                cmp edx,ecx                        ;减掉即将写的字节数
                jl preback

                sub edx,ecx
                shr ecx,2h                        ;将字节数转换为循环次数
                jz premain

                mov ebx,[esi+0]
        align 8
forwardblock1:                                ;处理未对齐的头部块
                movd mm0,[esi+0]
                movd [edi+0],mm0
                add esi,4
                add edi,4
                loop forwardblock1

premain:
                mov ebx,edx
                shr ebx,10h                        ;以64K字节为一个块进行n个块拷贝
                jz preback                        ;小于64K字节则跳到尾块处理
                push edx                        ;压入栈中待用

        align 8
mainloop1:
                mov eax,8192*8/128        ;预读128字节的次数
                xor ecx,ecx
        align 8
prefetchloop1:
                mov edx, [esi+ecx*8]                 ;预读
                mov edx, [esi+ecx*8+64]                ;预读下64字节
                add ecx, 16
                dec eax
                jnz prefetchloop1

                mov eax,8192*8/8        ;每个64K块中写64字节的次数
                xor ecx,ecx
        align 8
copyloop1:
                movq mm0,qword ptr [esi+ecx*8+00]
                movq mm1,qword ptr [esi+ecx*8+8]
                movq mm2,qword ptr [esi+ecx*8+16]
                movq mm3,qword ptr [esi+ecx*8+24]

                movntq qword ptr [edi+ecx*8+00],mm0
                movntq qword ptr [edi+ecx*8+8],mm1
                movntq qword ptr [edi+ecx*8+16],mm2
                movntq qword ptr [edi+ecx*8+24],mm3

                movq mm4,qword ptr [esi+ecx*8+32]
                movq mm5,qword ptr [esi+ecx*8+40]
                movq mm6,qword ptr [esi+ecx*8+48]
                movq mm7,qword ptr [esi+ecx*8+56]

                movntq qword ptr [edi+ecx*8+32],mm4
                movntq qword ptr [edi+ecx*8+40],mm5
                movntq qword ptr [edi+ecx*8+48],mm6
                movntq qword ptr [edi+ecx*8+56],mm7

                add ecx,8
                cmp eax,ecx
                jnz copyloop1

                add esi,8192*8                ;移动到下一个读入地址
                add edi,8192*8                ;移动到下一个写入地址
                dec ebx                                ;是否所有的块都已拷贝完
                jnz mainloop1

                pop edx                                ;取得压入栈中的字节数
                and edx,0FFFFh                ;取得剩余的字节数
                jz EXIT

preback:
                mov ecx,edx
                shr ecx,6                        ;剩余的64字节的块数
                jnz backwardblock1_64

                mov ecx,edx
                shr ecx,2
                jz EXIT

                mov ebx,[esi]                ;预读
                jmp backwardblock1_4

        align 8
backwardblock1_64:                ;处理剩余的64字节尾部块
                mov ebx,[esi+0]

                movq mm0,qword ptr[esi]
                movq mm1,qword ptr[esi+8]
                movq mm2,qword ptr[esi+16]
                movq mm3,qword ptr[esi+24]

                movntq qword ptr[edi],mm0
                movntq qword ptr[edi+8],mm1
                movntq qword ptr[edi+16],mm2
                movntq qword ptr[edi+24],mm3

                movq mm4,qword ptr[esi+32]
                movq mm5,qword ptr[esi+40]
                movq mm6,qword ptr[esi+48]
                movq mm7,qword ptr[esi+56]

                movntq qword ptr[edi+32],mm4
                movntq qword ptr[edi+40],mm5
                movntq qword ptr[edi+48],mm6
                movntq qword ptr[edi+56],mm7

                add esi,64
                add edi,64
                loop backwardblock1_64

                mov ecx,edx
                and ecx,03Fh
                shr ecx,2
                jz EXIT

                prefetchnta [esi]                ;预读

        align 8
backwardblock1_4:                ;处理剩余的4字节尾部块
                movd mm0,dword ptr [esi]
                movd [edi],mm0

                add esi,4
                add edi,4
                loop backwardblock1_4

                jmp EXIT
/*copy2:        ;对齐读未对齐写
                nop
copy3:        ;未对齐读对齐写
                nop
copy4:        ;未对齐读未对齐写
                nop*/
EXIT:
                sfence
                emms
        }
        return;
}

/*
把源地址的ARGB8888格式的图形数据转换为RGB565格式保存到目标缓冲
*/
void blt32to16(void* dst,const void* src,unsigned long nSize){
        const static __int64 mask = 0x00F8FCF800F8FCF8;
        _asm{
                mov edi,dst
                mov esi,src
                mov edx,nSize

                movq mm7,mask

                mov ecx,edi                        ;取得目标地址
                and ecx,0FFFFFFE0h        ;取得上一个32字节对齐的地址
                ;jz premain                        ;对齐则跳过头块处理

                add ecx,20h                        ;计算出下一个32字节对齐的地址

                sub ecx,edi                        ;计算出到下一个32字节对齐的地址之间的字节数
                and ecx,1Fh                        ;取低5位

                sub edx,ecx                        ;减掉即将写的字节数
                shr ecx,2h                        ;将字节数转换为循环次数
                ;jz premain

                mov ebx,[esi+0]
        align 8
forwardblock1:                                ;处理未对齐的头部块
                movq mm0,[esi+0]
                pand mm0,mm7

                movq mm1,mm0
                movq mm2,mm0

                add esi,4
                add edi,4
                loop forwardblock1

EXIT:
                emms
        }
}

/*
Blt指定值为COLORKEY的内存块
*/
void bltcolorkey(void* dst,const void* src,unsigned long nSize,unsigned long ColorKey){
        const static __int64 keymask = 0x00FFFFFF00FFFFFF;
        _asm{
                mov edi,dst                        ;目标地址
                mov esi,src                        ;源地址
                mov edx,nSize                ;串长度
                movd mm7,ColorKey        ;透明色

                movq mm5,keymask
                punpckldq mm7,mm7        ;将高32位与低32位合并
                pand mm7,mm5

                mov ecx,edi                        ;取得目标地址
                and ecx,0FFFFFFE0h        ;取得上一个32字节对齐的地址
                jz premain                        ;对齐则跳过头块处理

                add ecx,20h                        ;计算出下一个32字节对齐的地址

                sub ecx,edi                        ;计算出到下一个32字节对齐的地址之间的字节数
                and ecx,1Fh                        ;取低5位

                cmp edx,ecx
                jl preback

                sub edx,ecx                        ;减掉即将写的字节数
                shr ecx,2h                        ;将字节数转换为循环次数
                jz premain

                mov ebx,[esi+0]                ;预读源
                mov eax,[edi+0]                ;预读目的
        align 8
forwardblock1:
                movd mm0,[esi+0]        ;取得源像素
                movq mm6,mm7                ;取得四字掩码
               
                pand mm0,mm5
                movd mm1,[edi+0]        ;取得目的像素

                pand mm1,mm5
                pcmpeqd mm6,mm0                ;比较源像素与掩码

                pand mm1,mm6                ;取得目的像素中的显示部分
                pandn mm6,mm0                ;取得源像素中的显示部分

                por mm1,mm6                        ;合并
                movd [edi],mm1                ;写回内存

                add esi,4
                add edi,4
                loop forwardblock1

premain:
                mov ebx,edx
                shr ebx,5h                        ;以32字节为一个块进行n个块拷贝
                jz preback                        ;小于32字节则跳到尾块处理

                push edx                        ;压入栈中,将使用edx
prefetchloop:

        align 16
mainloop:
                and ebx,0FFFFh                ;清高16位的透明标志

                mov eax,[esi+0]                ;同头部算法相同
                movq mm6,mm7

                movq mm0,[esi+0]
                mov ecx,[edi+0]

                pand mm0,mm5
                movq mm1,[edi+0]

                pcmpeqd mm6,mm0
                pand mm1,mm5

                pand mm1,mm6
                pandn mm6,mm0

                por mm1,mm6

                movd edx,mm6                ;取得低32位到edx
                psrlq mm6,32                ;mm6高32位移到低32位

                or edx,edx                        ;测试是否为0(即源像素1是透明色)
                movd edx,mm6                ;将已移入低32位的数据移入edx
               
                jnz pixel34                        ;不为透明色则跳到下两个像素的处理
                or edx,edx                        ;测试是否为0(即源像素1是透明色)

                jnz pixel34                        ;不为透明色则跳到下两个像素的处理
                or ebx,010000h                ;两个像素都是透明色,设置透明标志

        align 8
pixel34:
                movq mm0,[esi+8]
                movq mm6,mm7

                pand mm0,mm5
                movq mm2,[edi+8]

                pcmpeqd mm6,mm0
                pand mm2,mm5

                pand mm2,mm6
                pandn mm6,mm0

                por mm2,mm6

                movd edx,mm6
                psrlq mm6,32

                or edx,edx
                movd edx,mm6
               
                jnz pixel56
                or edx,edx

                jnz pixel56
                or ebx,020000h

        align 8
pixel56:

                mov eax,[esi+64]
                movq mm0,[esi+16]

                movq mm6,mm7
                pand mm0,mm5

                mov ecx,[edi+64]
                movq mm3,[edi+16]
               
                pcmpeqd mm6,mm0
                pand mm3,mm5

                pand mm3,mm6
                pandn mm6,mm0

                por mm3,mm6

                movd edx,mm6
                psrlq mm6,32

                or edx,edx
                movd edx,mm6
               
                jnz pixel78
                or edx,edx

                jnz pixel78
                or ebx,040000h

        align 8
pixel78:

                movq mm0,[esi+24]
                movq mm6,mm7

                pand mm0,mm5
                movq mm4,[edi+24]

                pcmpeqd mm6,mm0
                pand mm4,mm5

                pand mm4,mm6
                pandn mm6,mm0

                por mm4,mm6

                movd edx,mm6
                psrlq mm6,32

                or edx,edx
                movd edx,mm6
               
                jnz write12
                or edx,edx

                jnz write12
                or ebx,080000h

        align 8
write12:
                test ebx,010000h                ;检查是否存在透明标志
                jnz write34                                ;是则跳过内存回写
                movntq [edi+0],mm1

        align 8
write34:
                test ebx,020000h
                jnz write56
                movntq [edi+8],mm2

        align 8
write56:
                test ebx,040000h
                jnz write78
                movntq [edi+16],mm3

        align 8
write78:
                test ebx,080000h
                jnz nextloop
                movntq [edi+24],mm4

        align 8
nextloop:
                add esi,32
                add edi,32

                dec bx
                jnz mainloop

                pop edx                               
                and edx,1fh                        ;取得剩余的字节数
                jz EXIT

preback:
                mov ecx,edx
                shr ecx,2                        ;剩余的4字节的块数
                jz EXIT

                mov ebx,[esi]                ;预读
                mov eax,[edi]                ;预读

        align 8
backwardblock:

                movd mm0,[esi+0]
                movq mm6,mm7

                pand mm0,mm5
                movd mm1,[edi+0]

                pcmpeqd mm6,mm0
                pand mm1,mm5

                pand mm1,mm6
                pandn mm6,mm0

                por mm1,mm6
                movd [edi],mm1

                add esi,4
                add edi,4

                loop backwardblock
EXIT:
                sfence
                emms
        }
}

30

主题

298

帖子

299

积分

中级会员

Rank: 3Rank: 3

积分
299
QQ
发表于 2004-4-28 22:30:00 | 显示全部楼层

Re:跪求高手指导sse

倒,不相关的也贴出来了。

32

主题

377

帖子

378

积分

中级会员

Rank: 3Rank: 3

积分
378
 楼主| 发表于 2004-4-29 13:34:00 | 显示全部楼层

Re: 跪求高手指导sse

不是说sse指令可以用128位的寄存器吗?这样一来不就可以一次copy4个32位色的点了吗?

30

主题

298

帖子

299

积分

中级会员

Rank: 3Rank: 3

积分
299
QQ
发表于 2004-4-29 16:22:00 | 显示全部楼层

Re:跪求高手指导sse

其实我的算法大部分是基于MMX,只有在内存回写的时候使用了SSE的MOVNTQ指令。  
    因为PIII,P4,及K7的FSB都是64位的,而且现在的内存也都还是64位的。所以用SSE的128位数据来回写数据并不能提高内存带宽。我的观点就是这样的啦。如有错误希望高手指正。

6

主题

444

帖子

457

积分

中级会员

Rank: 3Rank: 3

积分
457
发表于 2004-4-29 18:01:00 | 显示全部楼层

Re:跪求高手指导sse

sse没有movdqa/u指令,那是SSE2的指令。
我用SSE2也写了一下,发现尽管SSE2的movdqa比movq更有效率(两者的指令latency和throughput基本相同)但正由于hmhm说的原因,在内存利用率上两者没有区别,而memory latency是主导因素,所以两者在实际应用上速度基本没有差别(movdqa/movntdq甚至还要稍微慢一点)。
而hmhm说的‘在DDR266,外频133的机器上,大约能达到1.6-1.85G的带宽’倒让我很疑惑,你的配置倒底是什么啊,1.6-1.85G是否代表一秒可以copy 0.8-0.975G的东西啊?这个有点离谱,我写的code(跟你的差不多)在我的电脑上也就比这速度快一点(我的可是dual channel DDR266啊)

30

主题

298

帖子

299

积分

中级会员

Rank: 3Rank: 3

积分
299
QQ
发表于 2004-4-29 19:09:00 | 显示全部楼层

Re:跪求高手指导sse

我的配置是XP1700+,HY的DDR266 128M,KT333的主板。
     我所说的1.8G的带宽是在控制台程序里,关闭其它程序,并把进程调到实时级从而得到的数据。是拷贝1024*768*4BYTES大小的8字节对齐内存块,拷贝50次,再平均求出来的,大约峰值是0。95G这样。我只是用的timeGetTime来获得时间,可能会有一定误差。而且在实际的应用中,是不可能把进程调到如此高的优先级的。这仅仅是一个理想值罢了。

6

主题

444

帖子

457

积分

中级会员

Rank: 3Rank: 3

积分
457
发表于 2004-4-29 22:25:00 | 显示全部楼层

Re:跪求高手指导sse

那这速度也很惊人了。我用SSE2写的,拷贝256k 16byte对齐,拷贝速度一般也就在1.0-1.3G/s(也就是内存读写速度在2.0-2.6G),这样算来带宽利用率够低的(dual channel ddr266的理论极限是4.2G/s,这样算来也就是利用了一半多的带宽)
而你的那个峰值对带宽的利用率已经超过90%了。
按理说作为同时具有读写操作的内存带宽利用率不应该这么高的,读操作能够达到这个速度也很罕见(我测试了一下随机读的速度,大概在60%-90%间浮动)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-30 18:49

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表