高阶位 – 取出它们并将uint64_t转换为uint8_t

假设您有一个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操作。

谢谢您的帮助。

正如我在评论中提到的, 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 
 return ((x & 0x8080808080808080) * 0x2040810204081) >> 56; 

作品。 &选择要保留的位。 将所有位乘以最高有效字节,并将移位移至最低有效字节。 由于在大多数现代CPU上乘法很快,因此这不应该比使用汇编慢得多。

以下是使用SSE内在函数的方法:

 #include  #include  #include  int main (void) { uint64_t x = 0b0000000010000000000000001000000000000000100000000000000010000000; printf ("%x\n", _mm_movemask_pi8 ((__m64) x)); return 0; } 

适用于:

 gcc -msse 

您不需要所有单独的逻辑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; 

我不确定哪个在实践中会表现得更好,尽管我倾向于在第一个上下注 – 第二个可能产生更短的代码,但具有长的依赖链。

首先,您并不需要这么多操作。 您可以一次执行多个操作:

 x = (x >> 7) & 0x0101010101010101; // 0x0101010101010101 x |= x >> 28; // 0x????????11111111 x |= x >> 14; // 0x????????????5555 x |= x >> 7; // 0x??????????????FF return x & 0xFF; 

另一种方法是使用模数进行横向加法。 首先要注意的是x % n是基数n+1中的数字之和,因此如果n+12^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 >> 2是t / 4而<< 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; 

这似乎有效:

 return (x & 0x8080808080808080) % 127;