【问题标题】:High Order Bits - Take them and make a uint64_t into a uint8_t高阶位 - 使用它们并将 uint64_t 转换为 uint8_t
【发布时间】:2012-08-29 15:24:26
【问题描述】:

假设您有一个 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)
   )

又名移位 x,屏蔽,并为每个字节添加正确的位?这将编译成很多程序集,我正在寻找一种更快的方法……我使用的机器最多只有 SSE2 指令,我找不到有用的 SIMD 操作。

感谢您的帮助。

【问题讨论】:

  • 您可以重新解释单个字节,遍历它们并屏蔽单个位。不知道这是否更快,但也许编译器可以更好地优化它。
  • 也许您可以先用0x8080808080808080 进行掩码,然后乘以一个特定的常数,将这些位放在更方便的位置,也许可以在查找表中使用。
  • 是否需要结果,即8位序列作为数字?还是只检查 HO 位是否为 1 就足够了?
  • 是的,pmovmskb 完全符合您的要求。 IIRC 在 AVX2 中将有一个整数指令,您可以使用它来做同样的事情(收集位,忘记助记符)。
  • @AndyRoss 我正在写它,花了一段时间,因为我真的想把新指令放在那里:)

标签: c++ c assembly bit-manipulation


【解决方案1】:

正如我在评论中提到的,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 如果您添加正确的内联汇编(具有适当的约束)以使用此方法生成最佳代码。
  • @R.. 我愿意,但我以前从未这样做过。我尽量不要用 10 英尺长的杆子碰 GCC。我查看了这些约束,好吧,也许该代码会在一段时间内出现......也许
  • OK +1。如果我有时间研究如何做,我会添加它。
  • 这个 asm 没有简单的内在函数吗?
  • @rubenvb 你告诉我。我从来没有弄清楚如何从带有内在函数的寄存器中MOVQ
【解决方案2】:
return ((x & 0x8080808080808080) * 0x2040810204081) >> 56;

有效。 & 选择要保留的位。将所有位相乘到最高有效字节,然后移位将它们移动到最低有效字节。由于大多数现代 CPU 上的乘法运算速度很快,因此应该不会比使用汇编慢多少。

【讨论】:

  • 这实际上可能比 pmovmsk 快,这是一条相当慢的指令 AFAIR。
  • @drhirsch 2 周期延迟(AMD K10 上为 3)和 Core2 上 1 的吞吐量,一点也不差。即使只是这里的乘法也更糟。
【解决方案3】:

以下是使用 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

【讨论】:

    【解决方案4】:

    您不需要所有单独的逻辑与,您可以将其简化为:

    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;
    

    我不确定哪个在实践中会表现更好,尽管我倾向于打赌第一个 - 第二个可能会产生更短的代码,但依赖链很长。

    【讨论】:

    • 百万美元的问题是:gcc -msse 是否为此代码生成 pmovmskb? :)
    • 您可能希望将该常量限定为ULL,这样编译器就不会尝试使用带符号的值来欺骗。
    • @MarkB:这在 C++11 中不是必需的。
    • 我很确定 ULL 永远不需要。
    • 这在 C99 中也没有必要 - 因为 x 是无符号的,即使常量是有符号的,它也会被提升为无符号(即使常量的类型比 @987654328 更宽也是如此) @)。
    【解决方案5】:

    首先,您实际上并不需要这么多操作。您一次可以处理多个位:

    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 &gt;&gt; 7) &amp; 0x0101010101010101 像上面一样,你想对 7 位组求和,因此 t % 127 将是解决方案。但t%127 仅适用于高达 126. 0x8080808080808080 的结果,以上任何内容都会给出不正确的结果。我已经尝试了一些更正,但都不是很容易。

    尝试使用模数将我们置于前一个算法的最后一步是可能的情况。我们想要的是保留两个不太重要的位,然后得到另一个的总和,按 14 分组。所以

    ull t = (x & 0x8080808080808080) >> 7;
    ull u = (t & 3) | (((t>>2) % 0x3FFF) << 2);
    return (u | (u>>7)) & 0xFF;
    

    但是 t>>2 是 t/4 并且 (a % b)*c == (a*c % b*c),那么 (((t&gt;&gt;2) % 0x3FFF) &lt;&lt; 2) 就是 (t &amp; ~3) % 0xFFFC。但我们也有一个事实,即 a + b%c = (a+b)%c 如果它小于 c。所以我们只有u = t % FFFC。给予:

    ull t = ((x & 0x8080808080808080) >> 7) % 0xFFFC;
    return (t | (t>>7)) & 0xFF;
    

    【讨论】:

      【解决方案6】:

      这似乎有效:

      return (x & 0x8080808080808080) % 127;
      

      【讨论】:

      • 如果您设置了第一位并因此需要答案 >= 128,则不会。
      猜你喜欢
      • 1970-01-01
      • 2018-01-21
      • 1970-01-01
      • 2017-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多