目标是获得AVX寄存器的所有4个64位组件的XOR.它基本上与水平加法(_mm256_hadd_epi32())做同样的事情,除了我想要XOR而不是ADD.
标量代码是:
inline uint64_t HorizontalXor(__m256i t) { return t.m256i_u64[0] ^ t.m256i_u64[1] ^ t.m256i_u64[2] ^ t.m256i_u64[3];}解决方法 正如评论中所述,最快的代码很可能使用标量运算,在整数寄存器中执行所有 *** 作.你需要做的就是提取四个打包的64位整数,然后你有三个XOR指令,你就完成了.这可以非常有效地完成,并将结果留在整数寄存器中,这是您的示例代码建议您想要的.
MSVC已经为您在问题中显示的标量函数生成了非常好的代码:
inline uint64_t HorizontalXor(__m256i t) { return t.m256i_u64[0] ^ t.m256i_u64[1] ^ t.m256i_u64[2] ^ t.m256i_u64[3];}
假设t在ymm1中,则生成的反汇编将如下所示:
vextractf128 xmm0,ymm1,1vpextrq rax,xmm0,1vmovq rcx,xmm1xor rax,rcxvpextrq rcx,xmm1,1vextractf128 xmm0,1xor rax,rcxvmovq rcx,xmm0xor rax,rcx
…结果留在RAX中.如果这准确反映了您的需求(标量uint64_t结果),则此代码就足够了.
您可以使用内在函数稍微改进它:
inline uint64_t _mm256_hxor_epu64(__m256i x){ const __m128i temp = _mm256_extracti128_si256(x,1); return (uint64_t&)x ^ (uint64_t)(_mm_extract_epi64(_mm256_castsi256_si128(x),1)) ^ (uint64_t&)(temp) ^ (uint64_t)(_mm_extract_epi64(temp,1));}
然后你将得到以下反汇编(再次,假设x在ymm1中):
vextracti128 xmm2,1vpextrq rcx,xmm2,xmm2xor rax,rcx
请注意,我们能够忽略一条提取指令,并且我们确保使用VEXTRACTI128而不是VEXTRACTF128(尽管如此,this choice probably does not matter).
您将在其他编译器上看到类似的输出.例如,这里是GCC 7.1(假设x在ymm0中):
vextracti128 xmm2,ymm0,0x1vpextrq rax,1vmovq rdx,xmm2vpextrq rcx,rdxvmovq rdx,rdxxor rax,rcx
有相同的说明,但它们已经略有重新排序.内在函数允许编译器的调度程序按其认为最佳的顺序进行排序. Clang 4.0以不同的方式安排它们:
vmovq rax,xmm0vpextrq rcx,1xor rcx,raxvextracti128 xmm0,xmm0xor rdx,rcxvpextrq rax,rdx
当然,当代码内联时,这种排序总是会发生变化.
另一方面,如果您希望结果在AVX寄存器中,那么您首先需要决定如何存储它.我想你只是将单个64位结果存储为标量,如:
inline __m256i _mm256_hxor(__m256i x){ const __m128i temp = _mm256_extracti128_si256(x,1); return _mm256_set1_epi64x((uint64_t&)x ^ (uint64_t)(_mm_extract_epi64(_mm256_castsi256_si128(x),1)) ^ (uint64_t&)(temp) ^ (uint64_t)(_mm_extract_epi64(temp,1)));}
但是现在你正在进行大量的数据改组,否定了从矢量化代码中可能看到的任何性能提升.
说到这一点,我不确定你是如何让自己陷入这样一种情况,你需要首先进行这样的横向 *** 作. SIMD *** 作旨在垂直缩放,而不是水平缩放.如果您仍处于实施阶段,则可能需要重新考虑设计.特别是,您应该在4个不同的AVX寄存器中生成4个整数值,而不是将它们全部打包成一个.
如果您确实希望将4个结果打包到AVX寄存器中,那么您可以执行以下 *** 作:
inline __m256i _mm256_hxor(__m256i x){ const __m256i temp = _mm256_xor_si256(x,_mm256_permute2f128_si256(x,x,1)); return _mm256_xor_si256(temp,_mm256_shuffle_epi32(temp,_MM_SHUFFLE(1,3,2)));}
这仍然通过一次执行两次XOR来利用一点并行性,这意味着只需要两次XOR *** 作,而不是三次.
如果它有助于可视化,这基本上做:
A B C D ⟵ input XOR XOR XOR XOR C D A B ⟵ permuted input===================================== A^C B^D A^C B^D ⟵ intermediate result XOR XOR XOR XOR B^D A^C B^D A^C ⟵ shuffled intermediate result======================================A^C^B^D A^C^B^D A^C^B^D A^C^B^D ⟵ final result
在几乎所有编译器上,这些内在函数将生成以下汇编代码:
vperm2f128 ymm0,1 ; input is in YMM1vpxor ymm2,ymm1vpshufd ymm1,ymm2,78vpxor ymm0,ymm2
(在我第一次发布这个答案之后,我在上床的路上想出了这个,并计划回来更新答案,但我看到wim在发布它时打败了我.哦,这仍然是一个更好的方法比我第一次拥有,所以它仍然值得包含在这里.)
当然,如果你想在整数寄存器中使用它,你只需要一个简单的VMOVQ:
vperm2f128 ymm0,ymm2vmovq rax,xmm0
问题是,这会比上面的标量代码更快.答案是,是的,可能.虽然您使用AVX执行单元进行XOR,而不是完全独立的整数执行单元,但需要完成的AVX shuffles / permutes / extract更少,这意味着开销更少.因此,我可能还需要在标量代码上吃掉我的话,这是最快的实现.但这实际上取决于您正在做什么以及如何安排/交错指令.
总结以上是内存溢出为你收集整理的c – AVX中的水平XOR全部内容,希望文章能够帮你解决c – AVX中的水平XOR所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)