假设您有一个uint64_t,并且只关心uint64_t中每个字节的高位。像这样:
uint32_t的: 0000 ... 1000 0000 1000 0000 1000 0000 1000 0000 ---> 0000 1111
是否有比以下更快的方式:
return
(
((x >> 56) & 128)+
((x >> 49) & 64)+
((x >> 42) & 32)+
((x >> 35) & 16)+
((x >> 28) & 8)+
((x >> 21) & 4)+
((x >> 14) & 2)+
((x >> 7) & 1)
)
Aka移位x,屏蔽并为每个字节添加正确的位?这将编译到很多程序集,我正在寻找一个更快的方法...我使用的机器只有SSE2指令,我找不到有用的SIMD操作。
感谢您的帮助。
答案 0 :(得分:11)
正如我在评论中提到的那样,pmovmskb
会做你想要的。以下是您可以使用它的方法:
MMX + SSE1:
movq mm0, input ; input can be r/m
pmovmskb output, mm0 ; output must be r
SSE2:
movq xmm0, input
pmovmskb output, xmm0
我抬头看了新方式
BMI2:
mov rax, 0x8080808080808080
pext output, input, rax ; input must be r
答案 1 :(得分:10)
return ((x & 0x8080808080808080) * 0x2040810204081) >> 56;
的工作原理。 &选择要保留的位。将所有位乘以最高有效字节,并将移位移到最低有效字节。由于在大多数现代CPU上乘法很快,因此这不应该比使用汇编慢得多。
答案 2 :(得分:5)
以下是使用SSE内在函数的方法:
#include <xmmintrin.h>
#include <inttypes.h>
#include <stdio.h>
int main (void)
{
uint64_t x
= 0b0000000010000000000000001000000000000000100000000000000010000000;
printf ("%x\n", _mm_movemask_pi8 ((__m64) x));
return 0;
}
适用于:
gcc -msse
答案 3 :(得分:4)
您不需要所有单独的逻辑AND,您可以将其简化为:
x &= 0x8080808080808080;
return (x >> 7) | (x >> 14) | (x >> 21) | (x >> 28) |
(x >> 35) | (x >> 42) | (x >> 49) | (x >> 56);
(假设函数返回类型为uint8_t
)。
您也可以将其转换为展开的循环:
uint8_t r = 0;
x &= 0x8080808080808080;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
x >>= 7; r |= x;
return r;
我不确定哪个在实践中表现更好,但我倾向于在第一个上下注 - 第二个可能会产生更短的代码但是依赖链很长。
答案 4 :(得分:2)
首先,您并不需要这么多操作。您可以一次执行多个操作:
x = (x >> 7) & 0x0101010101010101; // 0x0101010101010101
x |= x >> 28; // 0x????????11111111
x |= x >> 14; // 0x????????????5555
x |= x >> 7; // 0x??????????????FF
return x & 0xFF;
另一种方法是使用modulo进行横向添加。首先要注意的是x % n
是基数n+1
中数字的总和,因此,如果n+1
为2^k
,则添加k位组。如果你开始
如上所述t = (x >> 7) & 0x0101010101010101
,您想要对7位组进行求和,因此t % 127
将成为解决方案。但t%127
仅适用于高达126的结果.0x8080808080808080以上任何内容都会产生错误的结果。我已经尝试了一些修正,没有一个容易。
尝试使用modulo将我们置于可能只有前一算法的最后一步的情况。我们想要的是保持两个不太重要的位,然后得到另一个的总和,按14分组。所以
ull t = (x & 0x8080808080808080) >> 7;
ull u = (t & 3) | (((t>>2) % 0x3FFF) << 2);
return (u | (u>>7)) & 0xFF;
但是t&gt;&gt; 2是t / 4且&lt;&lt; 2乘以4.如果我们有(a % b)*c == (a*c % b*c)
,那么(((t>>2) % 0x3FFF) << 2)
就是(t & ~3) % 0xFFFC
。但是,如果小于c,我们还有a + b%c =(a + b)%c的事实。所以我们只是u = t % FFFC
。给出:
ull t = ((x & 0x8080808080808080) >> 7) % 0xFFFC;
return (t | (t>>7)) & 0xFF;
答案 5 :(得分:0)
这似乎有效:
return (x & 0x8080808080808080) % 127;