Convert each bit in byte to first bit of each nibble in 32 bit int

时间:2017-04-02 00:43:14

标签: bit-manipulation opencl gpu

I have a byte b. I am looking for the most efficient bit manipulation to convert each bit in b to the first bit of each nibble in a 32 bit int x.

For example, if b = 01010111, then x = 0x10101111

I know I can do a brute force approach:

x = (b&1) | (((b>>1)&1)<<4) | ......

Edit: this for an OpenCL kernel for GPU

1 个答案:

答案 0 :(得分:1)

PDEP

正如用户harold在评论中提到的那样,PDEP完全你想要的指令 - 但它仅在x86上可用(据我所知),并且它在newest AMD chips上有 1 表现可怕。

LUT

除此之外,256 x 4字节条目的查找表似乎是合理的 - 代价是缓存子系统的1K压力。由于隐藏的高速缓存未命中成本,你会发现许多聪明人提倡反对LUT - 但如果这个特定操作实际上是“热门”,那么即使考虑到任何额外的未命中,它也可能变得最快。 / p>

与任何LUT解决方案一样,您应特别注意不仅要使用微基准测试,而且要在整个应用程序中对其进行基准测试,以评估内存压力的影响。

您还可以考虑一种折衷分裂LUT解决方案,该解决方案对字节的每个半字节使用一个或两个16项LUT,其结果计算如下:

int32 x = high_lut[(b & 0xF0) >> 4] | low_lut[b & 0xF]

这会将LUT的大小减小~11到32 2 ,因为我们有更少的条目,有些条目可以是2个字节而不是4个字节。

位操作

如果您真的想要一点操作解决方案,为了给您的章程留下深刻印象,您可以尝试以下内容:

  1. 将字节拆分为半字节,并使用乘以0x00001111(低半字节)和0x01111000(高半字节)将低(高亮度)半字节映射到低(高亮度)的一半4字节的单词,并将结果与​​oradd合并。因此,如果您的字节位为abcd efgh,则会有abcd abcd abcd abcd efgh efgh efgh efgh
  2. 之类的字
  3. and这个结果带有一个掩码,可以选出每个半字节中的位(虽然它通常不会在正确的位置)。掩码类似0x84218421,结果(二进制)类似于a000 0b00 00c0 000d e000 0f00 00g0 000h
  4. 现在使用减法的进位行为将不在高位的8位中的6位移动到正确的位置,例如:((x | 0x08880888) - 0x01110111) ^ 0x08880888
  5. 最后一步的基本思想是设置每个半字节的高位,并从半字节中减去1。例如,你有0b00半字节,它变为1b00 - 1 - 减法包含所有零,并在第一个停止,这是高位(b是零)或b如果是一个。因此,您可以根据所选位的值有效地设置高位。请注意,您不需要为ae执行此操作,因为它们已经在正确的位置。

    需要最后的xor因为上面实际上将高位设置为与所选位相反的值,所以我们需要翻转它。

    我没有尝试过,所以毫无疑问是错误的,但基本的想法应该是合理的。可能有各种方法可以进一步优化它,但它并不是太糟糕:几次乘法,也许是六次位操作。在具有慢速乘法的平台上,您可以找到第一步的另一种方法,它只使用1次乘法和一些更原始的操作,或者以多次操作为代价为零。

    1 吞吐量比英特尔低18倍 - 显然AMD选择不实施电路在硬件中执行PDEP,而是通过一系列更基本的操作来实现它。

    2 最大的减少是,如果您为高半字节和低半字节共享一个16项LUT,尽管这需要为高半字节查找的结果进行额外的移位。示例中显示的较小的减少使用两个16入口LUT:一个4字节一个用于高半字节,另一个用于低半字节,并且避免移位。