|
发表于 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
}
} |
|