【问题标题】:Best way to convert 8 boolean to one byte?将 8 个布尔值转换为一个字节的最佳方法?
【发布时间】:2015-02-07 13:30:31
【问题描述】:

我想将 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"

谁能给我一个更好的代码(更快)?

【问题讨论】:

  • 无所不能。
  • 你确定这有效吗?你在写第一位之前左移。
  • 你没有做的一件事是在开始之前清除a
  • @Blob:先换班才是前进的方向。
  • @Blob 我的代码运行正常,并正确保存数据。

标签: c++ performance algorithm byte bits


【解决方案1】:

如果您不介意使用 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));

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

【讨论】:

  • 我测试了这个命令,它似乎有更好的性能,但它返回错误的结果。当我给它带有位 11001000 的字符数组时,它返回我 19 这是不正确的。
  • @abdolahS 对我来说是正确的,还是你想要它反转?
  • 是的,我想要它以相反的顺序!我该怎么做。谢谢你的回答。
  • 谢谢哈罗德。它现在给了我正确的答案。我可以反转数组,但它不是优化的方式。我会用这个代替。
【解决方案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 作为附加是没有好处的:它甚至可能会绊倒你,在 a** 中咬你,在某些其他情况下。

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


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

【讨论】:

  • 为什么不 unsigned int “原则上”?
  • @NeilKirk:如果你的意思是,为什么不将unsigned int 作为字节类型,在大多数机器上unsigned 不止一个字节。它可以是一个字节,具体取决于平台。但通常会更多。并且考虑到 OP 将字节值存储在文件中的任务,如果他想有效地做到这一点,那么一个好的方法是一次输出一大块字节,一个字节序列。那么序列项需要一个精确的字节类型。
  • 抱歉,我指的是bits 的类型,但我现在看到您指的是a 的类型。
【解决方案3】:

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

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

还有一个技巧:structs 可以有位偏移,您可以在它们上使用union 来将它们误用作整数。

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

【讨论】:

  • 由于他只添加了一个 0/​​1 值,并且已知该位一开始就为零(如果累加器在开始之前被清除),因此添加和 ORing 之间没有有效的区别。跨度>
  • @HotLicks: 是的——他使用的是有符号整数,所以将 1 移到最高位会反转他的值的符号;那么,+1 不一定与 |1 相同。
  • 所以??会发生什么不同??
  • (我同意使用 OR 会更清晰。但在上述情况下,功能上没有什么不同。)
  • @HotLicks:我同意在上述情况下它没有任何区别——但是,OP 没有告诉我们他将使用的“将其保存到文件”机制。也许稍后会将 char 填充为 32 位有符号整数——在这种情况下,all 前导位为 1。
猜你喜欢
  • 1970-01-01
  • 2013-09-14
  • 1970-01-01
  • 2011-06-26
  • 2020-06-02
  • 1970-01-01
  • 2013-09-20
  • 2014-02-13
  • 2014-08-10
相关资源
最近更新 更多