我有这个代码:
__attribute__((target("avx2")))
size_t lower_than_16(const uint64_t values[16], uint64_t x)
{
__m256i vx = _mm256_set1_epi64x(x);
__m256i vvals1 = _mm256_loadu_si256((__m256i*)&values[0]);
__m256i vvals2 = _mm256_loadu_si256((__m256i*)&values[4]);
__m256i vvals3 = _mm256_loadu_si256((__m256i*)&values[8]);
__m256i vvals4 = _mm256_loadu_si256((__m256i*)&values[12]);
__m256i vcmp1 = _mm256_cmpgt_epi64(vvals1, vx);
__m256i vcmp2 = _mm256_cmpgt_epi64(vvals2, vx);
__m256i vcmp3 = _mm256_cmpgt_epi64(vvals3, vx);
__m256i vcmp4 = _mm256_cmpgt_epi64(vvals4, vx);
const int mask = (_mm256_movemask_pd((__m256d)vcmp1)) |
(_mm256_movemask_pd((__m256d)vcmp2) << 4) |
(_mm256_movemask_pd((__m256d)vcmp3) << 8) |
(_mm256_movemask_pd((__m256d)vcmp4) << 12);
if (mask != 0xFFFF) {
// found
return __builtin_ctz(~mask);
}
return 16;
}
基本上,给定一个包含 16 个元素的数组,我想找到第一个为真的元素的索引values[i] <= x
。如果找不到元素,则返回 16。
这是使用 AVX2 实现的,我使用 gcc 作为编译器。汇编代码如下:
lower_than_16:
vmovq xmm2, rsi
vmovdqu ymm1, YMMWORD PTR [rdi]
vpbroadcastq ymm0, xmm2
vpcmpgtq ymm1, ymm1, ymm0
vmovmskpd esi, ymm1
vmovdqu ymm1, YMMWORD PTR [rdi+32]
vpcmpgtq ymm1, ymm1, ymm0
vmovmskpd eax, ymm1
vmovdqu ymm1, YMMWORD PTR [rdi+64]
sal eax, 4
vpcmpgtq ymm1, ymm1, ymm0
vmovmskpd ecx, ymm1
vmovdqu ymm1, YMMWORD PTR [rdi+96]
sal ecx, 8
vpcmpgtq ymm0, ymm1, ymm0
or eax, ecx
or eax, esi
vmovmskpd edx, ymm0
sal edx, 12
or eax, edx
mov edx, 16
cmp eax, 65535
je .L1
not eax
xor edx, edx
rep bsf edx, eax
.L1:
mov rax, rdx
vzeroupper
ret
(可在此处查看: https: //godbolt.org/z/7eea39Gqv)
我看到gcc
每次展开迭代都使用相同的寄存器。但是,如果每次展开迭代都使用不同的寄存器,效率会不会更高ymm
,因为这样 CPU 就可以更轻松地并行执行这 4 个独立的比较?我知道 CPU 会进行一些寄存器重命名,但它是否足够智能,不会强制这些指令不并行执行?或者,如果使用不同的寄存器,会不会更容易/更高效?
多谢