Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 高阶位-取它们并将一个uint64\u t转换成一个uint8\u t_C++_C_Assembly_Bit Manipulation - Fatal编程技术网

C++ 高阶位-取它们并将一个uint64\u t转换成一个uint8\u t

C++ 高阶位-取它们并将一个uint64\u t转换成一个uint8\u t,c++,c,assembly,bit-manipulation,C++,C,Assembly,Bit Manipulation,假设您有一个uint64,只关心uint64中每个字节的高阶位。像这样: uint32\t: 0000 ... 1000万1000万1000万-->0000 1111 有没有比以下更快的方法: return ( ((x >> 56) & 128)+ ((x >> 49) & 64)+ ((x >> 42) & 32)+ ((x >> 35) & 16)+

假设您有一个uint64,只关心uint64中每个字节的高阶位。像这样:

uint32\t: 0000 ... 1000万1000万1000万-->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 ops


感谢您的帮助。

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

x &= 0x8080808080808080;
return (x >>  7) | (x >> 14) | (x >> 21) | (x >> 28) |
       (x >> 35) | (x >> 42) | (x >> 49) | (x >> 56);
(假设函数返回类型为
uint8\u 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;

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

正如我在评论中提到的,
pmovskb
满足您的需要。以下是您可以如何使用它:

MMX+SSE1:

movq mm0, input ; input can be r/m
pmovmskb output, mm0 ; output must be r
SSE2:

我以新的方式查找

BMI2:


下面是如何使用SSE intrinsics实现这一点:

#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;
}
这似乎有效:

return (x & 0x8080808080808080) % 127;

首先,你真的不需要这么多手术。一次可以对多个位执行操作:

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+1
2^k
,则添加了k个位的组。如果你从
t=(x>>7)&0x0101010101
如上所述,您希望对7位的组求和,因此
t%127
将是解决方案。但是
t%127
仅适用于126的结果。0x8080及以上任何内容将给出不正确的结果。我试过一些修正,没有一个是容易的

试图用模来把我们置于这样一种情况,即前面算法的最后一步是可能的。我们想要的是保留两个低有效位,然后将另一个的总和按14分组。所以

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


工作。&选择要保留的位。将所有位相乘到最高有效字节,移位将它们移动到最低有效字节。因为乘法在大多数现代CPU上都很快,所以它不应该比使用汇编慢太多。

您可以重新解释单个字节,循环遍历它们并屏蔽单个位。不知道这是否更快,但也许编译器可以更好地优化它。也许你可以先用
0x8080
屏蔽,然后乘以一个特定的常数,将这些位放在更方便的位置,也许可以在查找表中使用。你需要结果吗,即8位序列作为一个数字?或者只检查HO位是否为
1
就足够了?是的,
pmovmskb
正是您想要的。IIRC在AVX2中会有一条整数指令,你可以用它做同样的事情(收集位,忘记助记符)。@AndyRoss我正在写它,花了一段时间,因为我真的想把新指令放在那里:)而百万美元的问题是:
gcc-msse
是否为此代码生成
pmovskb
您可能希望将该常量限定为
ULL
,这样编译器就不会试图对有符号的值耍花招了。@MarkB:在C++11中这不是必需的。我很确定ULL在C99中也不是必需的-因为
x
是无符号的,即使该常量有符号,它也会被提升为无符号的(即使常数的类型比
uint64\t
宽,这也是正确的。)如果添加正确的内联asm(具有适当的约束),则+1使用此方法生成最佳代码。@R..I会的,但我以前从未这样做过。我尽量不使用10英尺长的杆来触碰GCC。我查看了这些约束,并且,好吧,代码可能会在稍后出现。无论如何,可能是OK+1。如果我有时间研究如何做,我会添加它。这个asm没有简单的内在特性吗?@rubenvb you te告诉我。我从来没有想过如何从带有内部函数的寄存器中
MOVQ
。如果您设置了第一个位,因此需要回答>=128,则不会。这实际上可能比
pmovmsk
快,后者是一条非常慢的指令。@drhirsch 2周期延迟(AMD K10上的3)Core2上的吞吐量为1,一点也不差,甚至这里的乘法也更差。
return (x & 0x8080808080808080) % 127;
x = (x >> 7) & 0x0101010101010101; // 0x0101010101010101
x |= x >> 28;                      // 0x????????11111111
x |= x >> 14;                      // 0x????????????5555
x |= x >>  7;                      // 0x??????????????FF
return x & 0xFF;
ull t = (x & 0x8080808080808080) >> 7;
ull u = (t & 3) | (((t>>2) % 0x3FFF) << 2);
return (u | (u>>7)) & 0xFF;
ull t = ((x & 0x8080808080808080) >> 7) % 0xFFFC;
return (t | (t>>7)) & 0xFF;
return ((x & 0x8080808080808080) * 0x2040810204081) >> 56;