Pet*_*des 24 x86 assembly sse simd avx
"最佳"意味着最少的指令(或最少的uops,如果任何指令解码到多个uop).机器码大小(以字节为单位)是相同insn计数的平局.
恒定生成本质上是一个新的依赖链的开始,所以延迟很重要.在循环内生成常量也很不寻常,因此吞吐量和执行端口需求也几乎无关紧要.
生成常量而不是加载它们需要更多指令(除了全零或全一),因此它会占用宝贵的uop-cache空间.这可能是比数据缓存更有限的资源.
Agner Fog优秀的优化装配指南涵盖了这一点Section 13.4.表13.10具有用于产生向量序列,每一个元素是0,1,2,3,4,-1,或-2,与从8位到64位单元大小.表13.11具有用于产生一些浮点值序列(0.0,0.5,1.0,1.5,2.0,-2.0,和位掩码为符号位.)
Agner Fog的序列仅使用SSE2,无论是设计还是因为它尚未更新一段时间.
使用短的非显而易见的指令序列可以生成哪些其他常量? (具有不同移位计数的进一步扩展是显而易见的而不是"有趣的".)是否有更好的序列用于生成Agner Fog列出的常量?
如何将128位immediates移动到XMM寄存器说明了将任意128b常量放入指令流的一些方法,但这通常是不合理的(它不会节省任何空间,并占用大量的uop-cache空间.)
Pet*_*des 25
全零:( pxor xmm0,xmm0或者xorps xmm0,xmm0,一个指令字节更短.)
全部:pxor.这是生成其他常量的常用起点,因为(像pcmpeqw xmm0,xmm0)它打破了对寄存器先前值的依赖性(除了旧的CPU,如K10和前Core2 P6).对于Agner Fog指令表中任何CPU上pxor的字节或双字元素大小版本的版本没有任何优势W,但pcmpeq需要额外的字节,在Silvermont上较慢,并且需要SSE4.1.
SO 确实没有表格格式,所以我只是要列出补充昂纳雾的表13.10,而不是改进版.抱歉.也许如果这个答案变得流行,我将使用ascii-art表生成器,但希望改进将被引入到指南的未来版本中.
pcmpeqQAgner Fog的表生成16位元素的向量,并用于PSLLB解决这个问题.例如,packuswb/ pcmpeqw xmm0,xmm0/ psrlw xmm0,15/ psllw xmm0,1生成一个每个字节都是的向量packuswb xmm0,xmm0.(这种移位模式,具有不同的计数,是产生更宽向量的大多数常数的主要方式).有一个更好的方法:
2(SSE2)作为左移一个字节粒度,因此只用两个指令(/ )生成一个字节向量paddb xmm0,xmm0. 对于其他元素大小的左移一个节省了一个字节的机器代码与移位相比,并且通常可以在比shift-imm更多的端口上运行.-2pcmpeqwpaddb
paddw/d/q(SSSE3)将all-ones(pabsb xmm0,xmm0)的向量转换为-1字节向量,因此只需要两条指令.我们可以使用//生成1字节.(添加与绝对的顺序无关紧要). 不需要imm8,但只需要保存其他元素宽度的代码字节而不需要3字节VEX前缀时右移.这仅在源寄存器为xmm8-15时发生.(总是需要一个3字节的VEX前缀,否则可以使用2字节的VEX前缀).set1(-1)set1(1)-1psubb2pcmpeqwpaddbpabsb
我们实际上也可以在生成pabs字节时保存指令:vpabsb/w/d/ VEX.128.66.0F38.WIG/ vpsrlw dest,src,imm.由于字移位而跨越字节边界移位的所有位均为零VEX.NDD.128.66.0F.WIG.显然,其他移位计数可以将单个设置位置于其他位置,包括符号位以生成-128(0x80)字节的向量.请注意,这4是非破坏性的(目标操作数是只写的,并且不需要与源相同以获得所需的行为).您可以将all-ones保持为常量,或者作为生成另一个常量的开始,或者作为源操作数pcmpeqw(以1递增).
甲的矢量pabsb字节还可以是(见先前段落)可以由任何饱和到-128,使用所生成的psllw xmm0, 2.例如,如果您已经有pabsb其他东西的矢量,只需复制并使用即可pabsb.从内存中加载的恰好正常饱和的常量是此的潜在目标.
甲的矢量psubb字节可以与生成0x80/ packsswb/ 0xFF00.我认为这是"非显而易见的",因为大多数设定的性质并没有让我想到只是将它作为每个单词中的值生成并按常规进行packsswb.
0x7f针对归零寄存器的(SSE2)可以右移1,但仅当值为偶数时才会右移.(它pcmpeqw对于舍入是无符号的,对于临时的9位内部精度.)这对于常量生成似乎没有用,因为0xff是奇数:psrlw xmm0, 9/ packuswb xmm0,xmm0/ packuswb/ pavgb产生的dst = (dst+src+1)>>1字节多于一个insn而不是shift /包.如果其他东西已经需要归零寄存器,则pxor xmm1,xmm1/ pcmpeqw xmm0,xmm0确实保存了一个指令字节.
我测试了这些序列.最简单的方法是将它们放入paddb xmm0,xmm0,汇编/链接,并在其上运行gdb. pavgb xmm0, xmm1,0x7f在每一步之后转储,以及单步指令(paddb或pavgb).在.asm模式中,你可以layout asm切换到矢量注册表的显示,但它几乎无用,因为你无法选择要显示的解释(你总是得到所有的解释,不能滚动,并且列不行在寄存器之间).不过,这对整数regs/flags来说非常好.
请注意,使用这些与内在函数可能是棘手的.编译器不喜欢对未初始化的变量进行操作,因此您应该使用display /x $xmm0.v16_int8告诉编译器您的意思.或者也许使用ni会让你的编译器发出一个si.如果没有这个,一些编译器会在使用前将未初始化的矢量变量归零,或者甚至(MSVC)从堆栈中加载未初始化的内存.
通过利用SSE4.1 layout reg或动态tui reg vec零或符号扩展,可以将许多常量更紧凑地存储在内存中.例如,_mm_undefined_si128()可以使用_mm_set1_epi32(-1)来自32位存储器位置的负载生成作为32位元素的128b矢量.内存操作数可以微融合pcmpeqd same,same,因此不需要任何额外的融合域uops.但它确实阻止将常量直接用作内存操作数.
C/C++ 内在函数支持使用pmovzx作为负载是可怕的:有pmovsx,但没有采用{1, 2, 3, 4}指针操作数的版本.你可以破解它,但它很丑陋,编译器优化失败是一个问题.有关详细信息和gcc错误报告的链接,请参阅链接的问题.
使用256b和(不是很快)512b常量,内存节省更多.但是,只有多个有用的常量可以共享缓存行时,这才非常重要.
FP等效于此pmovzx,需要F16C(半精度)功能标志.(还有一个存储指令,包含单个到一半,但没有半精度的计算.它只是内存带宽/缓存占用优化.)
显然,当所有元素都相同(但不适合动态生成)时,pmovzx或者AVX pmovz/sx/ AVX2 _mm_cvtepu8_epi32 (__m128i a)是有用的. uint32_t *可以采用内存源操作数,但必须是128b. VCVTPH2PS xmm1, xmm2/m64(SSE3)进行64位加载,广播以填充128b寄存器.在Intel上,它不需要ALU执行单元,只需要加载端口.(类似地,pshufd在负载单元中处理dword大小和更大的AVX 负载,而不使用ALU).
vbroadcastps当您要将掩码加载到寄存器中以便在循环中重复使用时,广播或者非常适合保存可执行文件大小.如果只需要一条指令,从一个起点生成多个相似的掩码也可以节省空间.
另请参阅For对于具有所有相同组件的SSE向量,即时生成还是预计算?这是在询问有关使用vpbroadcastb/w/d/q/i128内在函数的更多信息,目前尚不清楚它是否在询问变量的常量或广播.
我还尝试了一些用于广播的编译器输出.
如果高速缓存未命中是一个问题,请查看您的代码,看看pshufd当相同的函数内联到不同的调用者时,编译器是否有重复的常量.还要注意一起使用的常量(例如,一个接一个地调用的函数)被分散到不同的高速缓存行中.常量的许多分散负载比从彼此附近加载大量常量要差得多.
movddup和/或广播加载允许您将更多常量打包到缓存行中,将开销加载到寄存器中的开销非常低.负载不会在关键路径上,因此即使需要额外的uop,也可以在长窗口的任何周期使用自由执行单元.
clang实际上做得很好:v[p]broadcast不同函数中的单独常量被识别为相同,可以合并相同的字符串文字.请注意,clang的asm源输出似乎显示每个函数都有自己的常量副本,但二进制反汇编显示所有这些RIP相对有效地址都引用相同的位置.对于256b版本的重复函数,clang还使用pmovz/sx仅需要8B加载,代价是每个函数中的额外指令.(这就是set1,很明显clang开发者已经意识到尺寸对性能很重要,而不仅仅是因为_mm_set).IDK为什么它没有下降到4B常数pmovzx,因为那应该同样快.不幸的是,vbroadcast并不是简单地来自16B常量的其他功能.这可能是有道理的:某些东西的AVX版本可能只会将其一些常量与SSE版本合并.最好让SSE常量的内存页面完全冷,并让AVX版本将所有常量保持在一起.此外,在汇编或链接时要处理更难的模式匹配问题(但是已经完成了.我没有阅读每个指令来确定哪一个能够合并.)
gcc 5.3也合并常量,但不使用广播加载来压缩32B常量.同样,16B常数不与32B常数重叠.
| 归档时间: |
|
| 查看次数: |
1733 次 |
| 最近记录: |