请SSE/SSE2熟悉的前辈指点下指令,比较简单

shier2817 2015-03-24 05:05:05
我刚学用汇编来写SSE/2相关操作的东西,所以对指令的运用相当不熟练,写一条都要查好久。。。

SSE里有这么个指令:MASKMOVQ,而SSE2又加了这个指令:MASKMOVDQU;
这俩用法差不多,只不过一个针对MMX寄存器,一个针对XMM寄存器,位数不同而已,都是:

指令 源数据寄存器, 移位表寄存器

这样的格式,实际上指令作用是把 64或128位的源数据拷贝到对应位数的指针里,但命令里没有指针,指针要在edi里面提供;
而之所以特殊是这个指令不是移动全部64或128位数据,只移动指定的字节,所以就有了第二个操作数:移位表。
它是跟源数据相同位数的寄存器,比如64位就是mm(可以指定最多8个字节)、128位就是xmm(可以指定最多16个字节);
当然一般没人会在移动整个(8或16)字节时来这么复杂的搞,用其他完整移动的指令就好了。。

而移位表的表示法是取每个字节的最高位,比如最低位[0-7]的字节,bit7 = 1 表示复制,bit7 = 0 表示不复制,用十进制说就是第一个字节是128就表示复制;而32678就表示复制第二个字节 bit15 = 1;以此类推。。。

以上是我读手册里自己总结的,应该是这个作用把???
而它能用在很多复杂的情况下,不但移动整个数据高位几个字节,低位几个字节可以用,甚至随意自由的移动中间或相间隔的几个字节都可以,但我现在还没要求这么复杂。

我现在只需要用在从低位开始拷贝多少(1-7,或1-15)个字节就行,但要想实现这个,就要首先把移位表这个寄存器填充好,说了一大堆终于说到我要问的了,我想问下如何构造这个移位表啊???
准确的说,如何快速构造移位表???因为如果是使用通用寄存器与MMX/XMM寄存器交叉循环一点点的算,一个个字节为赋值的话(就是最傻瓜的原理),我肯定是会的。。。
但我觉得肯定有比较简单快速的方法的,甚至只通过MMX/XMM寄存器相关的位运算等指令左右移动啥的就可以实现,所以我说我的问题实际上并不复杂,但是对于32位数的位运算我脑子的反应就很不灵活,只会照搬照抄,别说一下子到了128位了。。。

上面移位表的构造原理说了是根据每个字节的最高位,而我自己感觉为了快速生成移位表,要复制的那个字节对应的其他7个位都是1也行,这样应该会构造的更快吧,比如255表示复制第一个字节,65536表示复制第二个字节……

这样的话其实我自己想了个最基本的简单方法,拿128位寄存器举例:
将一个xmm先置0,再 -1,这样128个位都是1咯,
然后 先 pslldq xmm, (16-n);
接着 再 psrldq xmm, (16-n);
其中 n = 要从低位开始复制的字节数,是不是就会把右边不复制的那些位弄成0了???
我不知道这样想对不对,也不知道这么算是不是比较快。。。
因为连最基本的我都有点诧异:
在x86汇编里,我们将一个32位寄存器每一位全都搞成1,也就是说 reg32 = -1 呗,这样可以:
mov eax, -1;
或者专业一点:
xor eax, eax;
dec eax
都可以,但是这个xmm寄存器怎么弄成128位全是1啊?莫非是:
xor eax, eax
inc eax
movd xmm1, eax
pxor xmm0, xmm0
psubq xmm0, xmm1
movdqa xmm1, xmm0
punpckldq xmm0, xmm1
哈哈,我觉得有点扯淡了,这样确实能把128位全部置1,但我觉得这肯定不会是构造移位表应该采用的方法,所以越想越糊涂,只好来请教前辈了。。。

教教我,如何能快速填充好移位表,用1-7位 或 1-15位都行,我明白套路即可。。。多谢!
...全文
771 14 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
Sandrer 2015-06-27
  • 打赏
  • 举报
回复
整个帖就楼主一人在回复, 那你不如直接到博客上写算了
「已注销」 2015-04-02
  • 打赏
  • 举报
回复
对了,我补充一句,在INTEL手册,第一卷的7.3.9.3小节,说了较新的CPU支持所谓的快速字符串操作(Fast-String Operation),通过CPUID指令可以检测(EAX=07H, ECX=0H):EBX[bit 9] is 1;当然还允许关闭这个快速操作什么的。 另外即使硬件支持貌似也要满足一定的前提条件,没太仔细读,因为写这么个底层功能没必要限制使用者的硬件,所以人家是否支持就不一定了,因此也不必深究,反正上面说的意思就是硬件和条件都满足后,会提升 rep movs /rep stos 这两个指令的性能。。。 有兴趣的可以翻翻指令手册。。。
「已注销」 2015-04-02
  • 打赏
  • 举报
回复
这个问题感觉越来越没意义了,我多种方式测试,SSE来做memcopy性能没任何提升,除非网上有一版其实是不判断严谨的长度,一直复制128bit,也就是16字节,直到copy长度为负停止,但这个貌似有点太胡闹了,很多时候目标地址后面的数据是有用的,我们不能不管不顾的将后面的数据覆盖掉。。。 #11其实说的对,我也想到不要即时的构造移位表,而是写好了,需要的时候查表应该是最佳的,因此我简单写了这么个表(没太仔细研究对不对,因为即使复制的数据是16的倍数,没有需要查表来获取剩余字节的时候,效率也是不佳的,所以就没再去搞):

align 16
SSE_MASK_TABLE \
	db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80h
	db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80h, 80h
	db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80h, 80h, 80h
	db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80h, 80h, 80h, 80h
	db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80h, 80h, 80h, 80h, 80h
	db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80h, 80h, 80h, 80h, 80h, 80h
	db 0, 0, 0, 0, 0, 0, 0, 0, 0, 80h, 80h, 80h, 80h, 80h, 80h, 80h
	db 0, 0, 0, 0, 0, 0, 0, 0, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h
	db 0, 0, 0, 0, 0, 0, 0, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h
	db 0, 0, 0, 0, 0, 0, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h
	db 0, 0, 0, 0, 0, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h
	db 0, 0, 0, 0, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h
	db 0, 0, 0, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h
	db 0, 0, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h
	db 0, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h, 80h
而#8说,我的memcpy怎么写的?特简单啊,就是rep movs,这样的:

push esi
push edi
mov edi, [esp+0ch]            ;目标指针
mov esi, [esp+10h]           ;来源指针
mov ecx, [esp+14h]           ;复制长度
mov edx, ecx
and edx, 03h
shr ecx, 02h
cld
rep movsd
mov ecx, edx
rep movsb
pop edi
pop esi
retn 000ch
我测试了不管数据大还是小,用SSE来写的比我上面这段慢3、4倍呢(必须是严谨长度的复制哈,不能是只考虑全16的倍数的长度或者覆盖不在长度范围的目标数据哦); 这里有篇帖子,就是说SSE对内存复制没有啥帮助的: http://bbs.bccn.net/thread-339988-1-1.html 虽然他其中的代码我觉得可以优化,但是道理说的是没错的。 帖子结了吧,要是有好建议、好想法、好思路和好代码,欢迎随时和我交流。。。
  • 打赏
  • 举报
回复
查表法,两条指令完成转换,但是要占用256字节的表空间,典型的以空间换取时间的算法。
IRQ_CRASH 2015-03-28
  • 打赏
  • 举报
回复
引用 9 楼 u013550545 的回复:
lz 你要觉得 sse 做内存复制很慢的话 我建议 在满足下列条件的情况下 你用标准库的memcpy 和 自己写的试一下 ... 看看那个快 ... src/des 地址十六字节对齐 ... 复制数据量 大于 512 bytes ... 标准库的memcpy 在满足这两个条件的情况下 会使用 sse 的 movdqa 来复制 ...
CopyUp:
;
; First, see if we can use a "fast" copy SSE2 routine
        ; block size greater than min threshold?
        cmp     ecx,080h
        jb      Dword_align
        ; SSE2 supported?
        cmp     DWORD PTR __sse2_available,0
        je      Dword_align
        ; alignments equal?
        push    edi
        push    esi
        and     edi,15
        and     esi,15
        cmp     edi,esi
        pop     esi
        pop     edi
        jne     Dword_align

        ; do fast SSE2 copy, params already set
        jmp     _VEC_memcpy
        ; no return
复制数据量 大于 512 bytes ... -> 128 bytes
IRQ_CRASH 2015-03-28
  • 打赏
  • 举报
回复
lz 你要觉得 sse 做内存复制很慢的话 我建议 在满足下列条件的情况下 你用标准库的memcpy 和 自己写的试一下 ... 看看那个快 ... src/des 地址十六字节对齐 ... 复制数据量 大于 512 bytes ... 标准库的memcpy 在满足这两个条件的情况下 会使用 sse 的 movdqa 来复制 ...
CopyUp:
;
; First, see if we can use a "fast" copy SSE2 routine
        ; block size greater than min threshold?
        cmp     ecx,080h
        jb      Dword_align
        ; SSE2 supported?
        cmp     DWORD PTR __sse2_available,0
        je      Dword_align
        ; alignments equal?
        push    edi
        push    esi
        and     edi,15
        and     esi,15
        cmp     edi,esi
        pop     esi
        pop     edi
        jne     Dword_align

        ; do fast SSE2 copy, params already set
        jmp     _VEC_memcpy
        ; no return
IRQ_CRASH 2015-03-28
  • 打赏
  • 举报
回复
引用 7 楼 shier2817 的回复:
实话说这个SSE不知道干毛用的,我测试了用他做简单操作或运算根本不能提升性能,比如写memcpy,就没啥意义,数据量小的时候根本不如基本指令写的,数据量大的时候仅提升非常小的一点点。。。 所以我就当学习了解就好了,没准备大量使用这些指令。。。 而开头提出的问题是我感觉 MASKMOVQ/MASKMOVDQU 这个指令特别好玩,但想具体实现才发现,这个掩码表的构造太费劲了。。。 可能一些比较复杂的运算会用到SSE/2吧,反正我暂时对其指令的使用不是那么感兴趣了。。。 intel手册吧能看个7,8分,因为我英文不太好,只能配合着翻译读的很头疼。。。 不过我倒是比较喜欢 xmm0~xmm7 这8个寄存器的哦。。。因为我感觉这是CPU寄存器,存取应该比内存里最快的“栈”要更快的吧,所以在写基本指令的的代码的时候,是不是可以将以前经常搞的 local xxxx / sub esp, xxx 这样申请堆栈存储局部变量的方式,改为将局部变量存放在这些寄存器里面啊,就算为了方便存取一个寄存器只用低32位,也有8个变量位置可用哦。。。这是不是会对效率有帮助呢???不知道我这样理解是不是扯淡???
。。。。 你的memcpy 怎么写的 ? 挺有兴趣的 ... 除了 内存对齐/写回模式/地址自增下的 rep movsd/movsb 指令 才能跟 movaps 的效率 相仿 ... 其余情况下 基本都是跑不过 SSE的 mov ...
「已注销」 2015-03-27
  • 打赏
  • 举报
回复
实话说这个SSE不知道干毛用的,我测试了用他做简单操作或运算根本不能提升性能,比如写memcpy,就没啥意义,数据量小的时候根本不如基本指令写的,数据量大的时候仅提升非常小的一点点。。。 所以我就当学习了解就好了,没准备大量使用这些指令。。。 而开头提出的问题是我感觉 MASKMOVQ/MASKMOVDQU 这个指令特别好玩,但想具体实现才发现,这个掩码表的构造太费劲了。。。 可能一些比较复杂的运算会用到SSE/2吧,反正我暂时对其指令的使用不是那么感兴趣了。。。 intel手册吧能看个7,8分,因为我英文不太好,只能配合着翻译读的很头疼。。。 不过我倒是比较喜欢 xmm0~xmm7 这8个寄存器的哦。。。因为我感觉这是CPU寄存器,存取应该比内存里最快的“栈”要更快的吧,所以在写基本指令的的代码的时候,是不是可以将以前经常搞的 local xxxx / sub esp, xxx 这样申请堆栈存储局部变量的方式,改为将局部变量存放在这些寄存器里面啊,就算为了方便存取一个寄存器只用低32位,也有8个变量位置可用哦。。。这是不是会对效率有帮助呢???不知道我这样理解是不是扯淡???
IRQ_CRASH 2015-03-26
  • 打赏
  • 举报
回复
额 ... 怎么赶脚 LZ可以洗洗碎了 ? 这两条指令 都没有提供关于 mem 的读写操作 ... 也就是说要想从新写入内存 还得 再用一次 movd/movq/movqda ... 你去看看 这几条指令的吞吐量和指令延迟 为毛感觉没多大效率提升的 ... 如果lz 想要过滤掉内存中指定的一个 byte/word/dword .... 估计还不如这样写 ... (我说估计是因为我没写过 无法测试效率 ...)
        jmp _SSE_ROUTINE_MAIN_LOOP
_SSE_ROUTINE_MAIN_LOOP_ALL_COPY
		movdqa [edi], xmm0 ; 完整的 一次 movdqa
_SSE_ROUTINE_MAIN_LOOP:		

		movdqa xmm0, [esi] ; 取源内存 至 xmm0 
		movdqa xmm3, xmm7 ; xmm7 之前准备好的 掩码 比如想要 过滤 0xFFFFFFFF 那么 xmm7 就是 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
		
		pcmpeqd xmm3, xmm0 ; 如果 里面有掩码字节 那么就会有 0xFFFFFFFF 
		movmskps ecx, xmm3 ; 提取 掩码命中次数 ... 如果 里面么有 掩码命中 那么 ecx 为 0 有的话 就不会是 0 
		
		test ecx, ecx ; 测试是否有掩码命中 ?
		je _SSE_ROUTINE_MAIN_LOOP_ALL_COPY ; 继续复制 ... 当前字节中没有要过滤的字节 ... 
#if 0       
		; 根据当前 pcmpeqd xmm3, xmm0 的时候 xmm3 中的 0xFFFFFFFF 情况 过滤指定的dword ... 		
#else
        ; 根据ecx 中的情况做张 表 ... 呵呵 这样可以精确到每次 要过滤的字节数 ... 
#endif
赵4老师 2015-03-26
  • 打赏
  • 举报
回复
《The Intel 64 and IA-32 Architectures Software Developer's Manual》 我只会复制粘贴,不会指指点点。
zara 2015-03-25
  • 打赏
  • 举报
回复
没做过这类,对这类指令也不熟;不过,要快的话,内存里预设了,直接载入到 xmm 不可以吗?
「已注销」 2015-03-25
  • 打赏
  • 举报
回复
自己写了个可以生成128位、即16字节选择移位表了(只从最低位开始连续选择,不考虑只选中间或高位的复杂情况):

call _GET_DWORD			;假设ecx 中存放的是要选择传送的字节数,其范围必须是 1-15(代码未对其检查)
movd xmm1, eax
add ecx, -4
jng _RET
call _GET_DWORD
pinsrw xmm1, eax, 02h
shr eax, 10h
pinsrw xmm1, eax, 03h
add ecx, -4
jng _RET
call _GET_DWORD
pinsrw xmm1, eax, 04h
shr eax, 10h
pinsrw xmm1, eax, 05h
add ecx, -4
jng _RET
call _GET_DWORD
pinsrw xmm1, eax, 06h
shr eax, 10h
pinsrw xmm1, eax, 07h
jmp _RET

_GET_DWORD:
xor eax, eax
cdq
@@:
mov al, 80h
inc edx
cmp edx, ecx
je @F
cmp edx, 04h
jnb @F
shl eax, 08h
jmp @B
@@:
ret
_RET:
;此时 xmm1 中就是移位表了。。。然后再调用 MASKMOVDQU xmm0, xmm1 即可

我觉得算是比较简单的了,至于效率我觉得肯定有提升的空间,因为现在的代码对 9-15字节的时候需要调用好几次子函数,配合其他指令应该流程会更简单些,但相应的判断跳转也会多的。。。 懂的前辈请给点建议。。。。
「已注销」 2015-03-25
  • 打赏
  • 举报
回复
哎,基本上我算是找到方法了,但却不尽人意,拿128xmm举例: 1、清空寄存器:pxor xmm0, xmm0 ;这个我想没人不知道; 2、置128bit全是1:pcmpeqd xmm0, xmm0 ;一句就可以了,也就是每个字节都是255 3、复杂一点的:利用解包 mov eax, 80h movd xmm0, eax ;最低字节为128,也就是该字节最高位 = 1,看赋值的是啥该字节就是啥了 punpcklbw xmm0, xmm0 ;最低2个字节分别 = 128 punpcklwd xmm0, xmm0 ;低位4字节分别 = 128,就是一个双字 punpckldq xmm0, xmm0 ; 低位8字节分别 = 128 punpcklqdq xmm0, xmm0 ;16个字节全都是 128,就是128位里面,每个字节最高位都是1.。。。 这么说,利用开始我说的移位原理,不管是采取2还是3方法,只要再左右移动即可了,但实际却很无奈: http://blog.csdn.net/shines/article/details/34837449 这个帖子说的不错,真是intel的大坑啊。。。。。 那么利用方法1,接着再用 pinsrw 这个指令也能实现,这个是将一个字复制给128bit中任意的word位置的; 可悲的是,同 pslldq 和 psrldq 左右移动指令一样,都需要一个 imm8 的立即数,又是坑,立即数就表示必须的固定的常数,死的,根据运算结果动态获取的数不行,不管是保存在通用寄存器还是内存地址都不行,只能把数写死就意味着要写N条判断跳转,像 pinsrw 指令就得写至少7个判断(因为最大15个字节的话就需要用到第7个字),然后每个判断分支写1-7条pinsrw 指令,里面是不同的立即数。。。。。。。。INTEL是耍我们玩吧??? (实际上还有pinsrwb/pinsrd/pinsrq 这几个指令,分别针对字节、双字和四字的,但这些都是sse4.1才有的,现阶段用这些指令兼容性问题太大,只有 pinsrw 这个指令是sse2指令,应该在现今的CPU上都能通过) 类似的指令其实还有很多,psllw、pslld、shufps、shufpd、pshuflw、pshufhw等等不列举了,都需要用到一个立即数,这种指令用起来真是太TM坑爹了。。。 不知道玩sse/mmx的前辈有啥更好的处理方式么?
「已注销」 2015-03-24
  • 打赏
  • 举报
回复
晕,补充一下,按照我前面那种夸张的思路构造的128位全是1的寄存器,然后再用 pslldq 和 psrldq 左右移动一下,确实可以实现。。。 但第一是这种方法构造移位表我觉得肯定不对,应该有更简单快速的方式,应该是有的指令我没掌握或者不会用。。。 再一个就是我要补充问一下的,这个 pslldq 和 psrldq 第二操作数要求 imm8,就是8位立即数。。。郁闷,只允许直接提供立即数啊,不让传递通用寄存器,ecx不行,cl也不行,byte ptr 也不行。。。那这就没意义了啊,因为我的需求是动态算出要拷贝几个字节的,算出来的数肯定是在寄存器或内存里啊。。。郁闷啊,这如何解决???

21,497

社区成员

发帖
与我相关
我的任务
社区描述
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
社区管理员
  • 汇编语言
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧