如果您确实需要这个(并且无法通过重新排列数据来避免它),您可以完全/安全地模拟_mm_srlv_epi32
不会破坏任何高位或低位。
对于编译时常量计数,您可以将左移和右移与其中的大多数混合使用。
可能是不好的选择:
-
解压为标量:恶心。对于编译时常量计数来说有点糟糕,但对于运行时变量计数来说更糟糕,特别是如果您必须解压计数向量的话。没有 BMI2 的 x86 可变计数班次shrx
具有笨拙的语义并在 Intel SnB 系列上解码为多个微指令。他们还额外收取mov
输入班次计数的说明cl
如果它还不在那儿的话。
-
进行单独的移位,然后混合以从移位了该量的向量中获取元素。这不太好,但是您可以通过在复制不需要的元素时将其归零来降低混合成本。 (例如,如果已知高位元素为零,则复制pshufd
得到一个向量{0,22,0,0}
从起始向量{11,22,33, 0}
,并重复{0,0,33,0}
.)
因此,将您未使用的高位元素归零,2x pshufd 复制+将零打乱到位,3x psrld 具有不同的计数,并取出您未复制的向量中的其他元素,然后将 3 个向量重新组合在一起。 (如果您不保留向量的一个元素未使用,这需要更多工作。)
根据代码的其余部分和微架构,使用 shuffle 代替 MOVDQA+PAND 可能不值得。如果任何元素使用相同的班次计数,则此选项变得更有吸引力。
此外,您可以将低元素混合到向量中movss
,并将低半部分与movsd
。它们使用随机播放端口,因此随机播放吞吐量可能是一个问题。这实际上可能非常可靠。
希望有更好的选择。
__m128i srlv435_sse4(__m128i v)
{
__m128i rshift = _mm_srli_epi32(v, 3); // v >> 3
// differences in shift count by multiplying by powers of 2
__m128i vshift = _mm_mullo_epi32(rshift, _mm_setr_epi32(2,4,1,0)); // [ x >> 2, y >> 1, z >> 3, 0 ] Except with low bits truncated.
__m128i shift2 = _mm_srli_epi32(vshift, 2); // [ x >> 4, y >> 3, z >> 5, 0 ]
return shift2;
}
这很好,因为它可以就地运行,编译器不需要任何 MOVDQA 指令来复制寄存器,即使没有 AVX1。
注意SSE4.1 _mm_mullo_epi32
速度不快:Haswell 上的 p0 为 2 uops:10c 延迟,每 2c 吞吐量 1 个。 Skylake 上的吞吐量更好,其中 2 个 uops 中的每一个都可以在 p0 或 p1 上运行,但仍然依赖于 10c 延迟。 (http://agner.org/optimize/ http://agner.org/optimize/以及其他链接x86 /questions/tagged/x86标记维基百科。)
这在 Haswell 之前有更好的延迟pmulld
是单 uop 指令(~5 个周期,1c 吞吐量),而不是 10 个周期的 2 个相关 uop。
在 AMD Bulldozer 系列和 Ryzen 上,对于 pmulld,延迟 = 4 或 5,吞吐量 = 每 2c 1。
我没有检查端口与矢量移位的冲突。
没有SSE4.1,您可以使用2x SSE2_mm_mul_epu32
一次进行 2 次乘法。为了排列奇数元素(1 和 3),pshufd
将它们复制+洗牌到位置 0 和 2,其中pmuludq
寻找他们。
这会从偶数 2 个 32 位元素产生 2 个 64 位结果,因此您不需要预移位来避免溢出。这也意味着当移位计数之间的差值大于最小移位时可以安全使用,因此SSE4.1方式无法将所有需要的位保留在保留位最多的元素中。
// general case: substitute in *any* shift counts and it still works.
__m128i srlv_sse2(__m128i v) // [x y z w]
{
__m128i vs_even = _mm_mul_epu32(v, _mm_setr_epi32(1U<<1, 1U<<2, 1U<<0, 0)); // [ x<<1 z<<0 ] (64-bit elements)
// The 4 (1U<<2) is unused, but this lets us share a constant with the SSE4 version, saving rodata size. (Compilers optimize duplicate constants for you; check the disassembly for same address)
vs_even = _mm_srli_epi64(vs_even, 5); // [ x>>4 0 x>>5 0 ] (32-bit elements ready for blending with just an OR)
__m128i odd = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1));
__m128i vs_odd = _mm_mul_epu32(v, _mm_setr_epi32(1U<<(32-3),0,0,0)); // [ (y<<32) >> 3 0 ] (64-bit elements)
// If any elements need left shifts, you can't get them all the way out the top of the high half with a 32-bit power of 2.
//vs_odd = _mm_slli_epi64(vs_odd, 32 - (3+2)); // [ garbage, y>>3, 0, 0 ]
// SSE2 doesn't have blend instructions, do it manually.
__m128i vs_oddhi = _mm_and_si128(vs_odd, _mm_setr_epi32(0, -1, 0, -1));
__m128i shifted = _mm_or_si128(vs_even, vs_oddhi);
return shifted;
}
这里有一些明显的优化:
您的情况没有使用第四个元素,因此第二个乘法是没有意义的:只需移位并使用 AND 掩码来清除高位元素。vs_odd = _mm_srli_epi32v, 3);
并使用0,-1,0,0
作为您的 AND 掩码。
不要左移 1 和 0,而是将 x 添加到自身并保持 z 不变。通过将高 64 位清零来复制向量非常便宜(movq
),但不如movdqa
(在带有 mov-elimination 的 CPU 上)。
__m128i rshift = _mm_srli_epi32(v, 3); // v >> 3
__m128i xy00 = _mm_move_epi64(rshift);
__m128i vshift = _mm_add_epi32(rshift, xy00); // [ x >> 2, y >> 2, z >> 3, 0 ]
但这不能处理y
。我们可以隔离y>>2
from vshift
并再次添加以产生y>>1
。 (但记住不要使用旧的y>>3
from xy00
).
我们也可以考虑使用_mm_mul_epu32
(pmuludq
)一次,然后复制+shift+AND 进行其他步骤(从原始复制v
代替rshift
缩短 dep 链)。这对于您的情况很有用,因为您没有使用顶部元素,因此只有一个有效的奇数元素,因此您不需要变量移位。
结合movq
, movss
, and movsd
,从基本上分别移动这 3 个元素可能会获得更多的东西。端口压力、延迟、uop 计数(前端吞吐量)等之间需要权衡。例如我在想
movdqa xmm1, xmm0
psrld xmm0, 3 # [ x>>3 y>>3 garbage ]
psrld xmm1, 4 # [ x>>4 y>>4 garbage ]
movss xmm1, xmm0 # [ x>>3 y>>4 garbage ] # FP shuffle
psrld xmm0, 2 # [ garbage z>>5 ]
movsd xmm0, xmm1 # [ x>>3 y>>4 z>>5 ] # FP shuffle
例如,Haswell 每个时钟吞吐量只有 1 个移位,因此这并不好。与乘法选项相比,它具有相当好的延迟。这在 Skylake 上很好,其中 2 个端口可以运行矢量立即移位。
FP 在整数指令之间进行洗牌在除 Nehalem 之外的 Intel CPU 上表现良好(每种方式都有 2 个周期的旁路延迟延迟损失,但吞吐量仍然可以)。我认为AMD也很好。
当然,所有这些 CPU 都有 SSE4.1,因此如果您使用动态运行时调度,SSE2 版本只需在 Core2 / K10 上工作。 (我猜是较旧的 Atom,或者其他什么)。
Godbolt 上的代码 + asm 输出 https://gcc.godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAKxAEZSBnVAV2OUxAHIBSAJgGY8AO2QAbZlgDU3fgGE8AWwXCCxYQDoEM7NwAMAQT36A%2BsYW1eADjySGxUQDcALPwCsxhg0xOIp81ZsHAEojbgB2ACEjSRjJPwtrSWIGBDwAMwJpfgAROKUPezxjTAAHPH5eCAdSSX4Q/gjYgHomyQcsnX5sWujm1vw0tMxiTBFMBklhW1SMyTRmIUyAIwBPSQVmUQI8EtEV4WBJVckS1AB3YYnUNMleXpj4gLaU9MyZXLMFM03RVGKyioQZIzAg1T4eTCqf7lSq8UhOUj0XRBeqNFrSVyNAAeHW0txqkjW2jx9EkAC9cV1ajVdBjcpJsFj2CVMmdCAhJL8zkdCBNVAtkABDAiYdDqe5xMwJGwvDK8LIffJ2URFUowqqy0G3VGxXV6/X69HcTGSHHEqkImJErp4/g1Cnm7quGl0iVJSGsITTV53BqhMLZUIGdHAUbDQWiOaCrwgWzMJYMbYEZgiyZegBUgqEK3T3tm80WEyz6EmmUTeFEkbOJAA1gxxQZHollQ4PF5Kk3AkEYkbMTjCTEKZIzsbAwZwlEDLrO89ig5Rgq8l8NqJ/sxAdUlxCoWrAbQAKoyWRH0kHo9HuGSM9yI%2B6GkomSNPoY7HnuS0XVkt%2ByWmjmIQAA2JwAFolkISRMFETAFFGAgGBCKdnwAFQQTBJCcSQIGvY85F4bs8AmBZmC8dAaiWFNJAIVIJiguDJGI6ZBRGSRBTmVAhETLNWXZSjUMkABlfjsAw%2BdkjwdiagYQUHAOJIMGFViGDwMlMHUf9ZFQBQyig5JJFQFlFGUtD0GYXY8CFVM0A4gguImNISEJFhHzmVDkBrXjjII6MvAUJY9kkeziFsQUYJY9B0BGTwEP0XUHAYOcF3eLdlVVMogI1BKhBqVwdUkXtXxtLoMMkWkYixR1XBiX9XHpCAKlA8DIOg2CJhGQV0DWQKjigoR8CEQ42SoyQqGIzIs0kAB5AAlaKJRnDASyS8EUmYQYoOhDcwQAWS24x%2BIACX3AAxI6ABlsDqmo7SvGpaAfP1EIeKUnji4wFsXLcVzXTbkshYgNsqbCjzq3hgLqUg70h%2B6nx7VpjUaCAVm/Cpu0dWpdWq2qgIazImpgwtZsevLWgASRuLM1jxlrJCETBRU5TBZk1BgahWFgoyEMBOEyUNMio6CWMrDzh0FNYWD5viiBKPSbn5yRUmADkEAjG5Bo5Vj6rAzJTguILrluBsYr6V73qWpVK1SvB0pN8KrvlYDMP4PgIjw3K9XyyRgCYpZBVDAkkcK7BrpKl1RyDI3YYEoT5XQVBxk57n5ektC/NGEthETYhmGQbZ2JZyRY9LdYs2YCM9kN6dnsSG30FSRdwWLDw8ASDKFrBJU/oBiA70kYDSR7vvobmquZRBemza%2BEgm5b17MHnLLZwW1JUXDvURmTYgvU1UVH39MdDHHAR0hK8P0QWNJhAI1D06waMjBnFsXHcTxMA7Ee2m7YmX37NZyRiEcar%2BknBHSU/hEhkk1PXJUhQu6bhys5A0z54ae29r7TANQvbEB9n7P%2BaNnQoKwWg1044iYzmBK8KBXwUqwKum7RBHtyqB2ugHToQcagyCOhwjhxCD4gPREdEmAANLa2BYxUQIpMCYPs3JnCYugfOFwRaLAHNvEsUFPCSVQNIUh78sTtRLDECeZhUDzmhOlchGQV46LATYLEKxIFGP0V3CxWo9HhTdgwx0l4WHaGulw/gnCAk8LdAaOxDicjJRgbuSoYSQS3ToZ4phrNHR%2BICf4o6PDibLSICMDw6BV6xHXp6U09iQS72DOmYeNiSm6FKpQ4xrY1TWysSAmccUQT1KcdEqoNQ7G1LoUgk0ZpA74itJSbol4HQjOupjKp0oBx6kcb1aeVgIB9J7stTu3SB793vC0xBsVwmKi%2BF0gElR2mvFZgMhh4zRkDjRqSKZrDqQlWCdY%2BZmp5RGOod0i5GQah4QQZHZBwznmWnudM%2B0tz8GzKJu6DeW8QS%2BmAXqdMTQ97hz4PwNO6ROBBFIKILgrhOCkCEFwXQJLUBcGPLwKItLbAsDYGhLFtASUEHJXi/FNYQD8EsOofguh%2BAAVoAATgAgBVwugnCuBFXaQlnAnAkrJZwClpAqWcBJQwEAd52UqrxaQOAsAkBoC0hWYY5BKAmu0sMEAwAwhwgvlsS4lAlgctIGBIQTEVhcFZaQE1%2BMCATSEHsN1WAFBZmAFBUNeARg5zwPOLVerSCYCxJgZAKYOCcF9SoSCPq2VqAUHm/FKoEzwHxfpXOHEuDASZL3RM%2BScjACEMwEVIre4AHUy69wmvwXuChky03eAwGsKxRCChrGhYCCgX4YSnZ4Lw/BNWMvYHQYtRKlVuvVViSwAFgJAU9sgZAkgwjqHlBAXAhAHIspqBpU1OlpACDumyjlKJSDcqsPygCAqnBhFqbQACvBdBhFcGEAlXBFWks3VwTV2rSC6opfiw1iAUCaWtWQCgEArVmuICgMd/VJV3kdSKZILq3Ueq9Xmv1mkA1BpDUmsNEao30ZjWm7YCa3UprTRmyjOb5W%2BtUIoItBK8ClsgOWgyedq21uAvW94TaW1tuAp2oWwEe19oHZgIdI6x0TpiHOrwEGmCsBXXdMDnBiWQaTVunde6MJiAjZIVw6hdDOcwhenJD7%2BCklvWhzzeFn16tfdy/g/B%2BUhfCxFiLZmIPKtVeqmDOqX1rs4LwDdVnoNwaS6QUSSl2IgCcEAA%3D%3D%3D