将8个布尔值转换为一个字节的最佳方法是什么?

时间:2015-02-07 13:30:32

标签: c++ performance algorithm byte bits

我想将8个布尔值保存到一个字节,然后将其保存到文件中(这项工作必须针对非常大的数据完成),我使用了以下代码,但我不确定它是最好的(在速度和空间方面):

int bits[]={1,0,0,0,0,1,1,1};
char a='\0';
for (int i=0;i<8;i++){
  a=a<<1;
  a+=bits[i]
}
//and then save "a"

任何人都可以给我一个更好的代码(更快的速度)吗?

3 个答案:

答案 0 :(得分:3)

如果您不介意使用SSE内在函数,那么_mm_movemask_epi8非常适合。它使用16个字节,但您可以将其他字节设置为零。

例如(未经测试)

__m128i values = _mm_loadl_epi64((__m128i*)array);
__m128i order = _mm_set_epi8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                             0, 1, 2, 3, 4, 5, 6, 7);
values = _mm_shuffle_epi8(values, order);
int result = _mm_movemask_epi8(_mm_slli_epi32(values, 7));

这假设数组是一个字符数组。如果你不能做到这一点,它需要更多的负载和包,它会变得有点烦人。

答案 1 :(得分:2)

关于

  

任何人都可以给我一个更好的代码(更快的速度)

你应该衡量。序列化到文件的速度的大部分影响是i / o速度。您对这些位的处理可能会产生无法估量的小影响,但如果它有任何影响,那么这可能主要受到您对布尔序列的原始表示的影响。


现在关于给定的代码

int bits[]={1,0,0,0,0,1,1,1};
char a='\0';
for (int i=0;i<8;i++){
a=a<<1;
a+=bits[i]
}
//and then save "a"
  • 原则上使用unsigned char作为字节类型。
  • 原则上再次使用位级OR,|运算符。
  • 使用前缀++,是的,也是原则上的。

第一点的“原则上”是因为在实践中,您的代码不会在具有符号和大小的任何机器上运行,或者在有符号整数的补码表示中运行,其中char已签名。但我认为在代码中准确地表达所做的事情通常是一个好主意,而不是将其重写为稍微不同的东西。这里的目的是处理位,一个无符号字节。

对于位级OR的“原则上”是因为对于这种特殊情况,在级别OR和加法之间没有实际区别。但总的来说,用代码写出表达方式是个好主意。然后写一个小级OR或者作为一个补充是没有好处的:它甚至可能会绊倒你,在另一个背景下咬你的**。

前缀++的“原则上”是因为在实践中,当表达式结果未被使用时,编译器将为基本类型优化前缀和后缀++。非常相同的机器代码。但是,再一次写出想要表达的内容通常会更好。询问原始值(后缀++)只会在您不使用原始值时误导代码的读者 - 并且与位级别OR表示为加法时,纯增量表示为postfix ++可能会绊倒你,在a **中咬你,在其他情况下,例如用迭代器。


显式编码移位和ORing的一般方法在我看来很好,因为std::bitset不支持从一系列布尔值初始化(只从文本字符串初始化),所以它不会除了你的工作。但通常,检查标准库是一个好主意,是否支持任何人想做的事情。甚至可能会发生其他人在这里使用一些我没有想到的基于标准库的方法。 ; - )

答案 2 :(得分:-1)

+=运算符替换为|=,这是逐位运算(实际上您想在此处执行此操作)。 如果可能,请使用unsigned char作为您的真值。

除非您想手动展开循环和/或使用SIMD内在函数,否则这将是编译器优化最多的解决方案。

还有另一个技巧:struct s可以有位偏移,你可以使用union来滥用它们作为整数。

顺便说一下:你的代码有问题。你先换班,然后写;你使用加法,但是signed char,第7位和第8位肯定会出错(假设你错误地过早移位;如果你做得恰当,只有第8位会造成危险)。