【问题标题】:Help me improve this C++ bit-buffer processing code帮我改进这个 C++ 位缓冲处理代码
【发布时间】:2009-08-20 16:57:59
【问题描述】:

我正在编写一个函数来处理传入的 32 位缓冲区,表示在将数据与相应存储的 32 位缓冲区进行比较时发生变化的数据。变化位的位置表示需要处理的数字(即值 8 表示位 3),以及变化是 0->1 还是 1->0。这是当前的实现,请帮助我改进它!请注意,这不是实际代码,它已被简化为与上下文无关。

uint32_t temp = oldBuffer ^ newBuffer;
uint32_t number = 0;
while (temp != 0)
{
    if (temp & 0x1)
    {
        uint32_t bitValue = 0;
        if ((newBuffer& (1 << number)) != 0) bitValue = 1;
        processNumber(number, bitValue);
    }
    number++;
    temp = temp >> 1;
}
oldBuffer = newBuffer;

现在它可以工作,但我不喜欢它必须通过检查位 1 并在整个过程中移动来检查每个位。如果保证只有 1 位设置,那并不难弄清楚,但事实并非如此。

编辑:对于 Neil,我想我希望找到一种方法来在恒定时间内获取 XOR 之后的位位置,而不是一直移动通过​​缓冲区并逐个检查位。

【问题讨论】:

  • 可以使用标准库吗?

标签: c++ c bit-manipulation


【解决方案1】:
uint32_t temp=oldBuffer^newBuffer, ntemp=newBuffer;
for(int b=0;temp;++b,temp>>=1,ntemp>>=1)
    if(temp&1) processNumber(b,ntemp&1);

【讨论】:

  • +1,有些空格对提高可读性有很大帮助;D
  • 很好 - 一件(非常)小事:b&lt;32 的测试是不必要的。当test 变为 0 时停止循环就足够了(不知道编译器是否足够聪明,可以在优化时解决这个问题)。
  • 这不是相同的代码,只是形式更简洁。换句话说,我不希望它以相同的速度运行(当 b
  • 好的,迈克尔,谢谢。我会编辑它。而且,六个字母变量,这是一个口味问题。我发现较短的代码更具可读性,因为我看到更多的代码而不移动我的眼睛;-)。只有当我实际分隔逻辑时,空格才会提高我的可读性(例如,上面&amp;&amp; 周围的空格)。
  • 娄,移位数相同,但位数不同。
【解决方案2】:

您可能会通过使用一种或多种“bit twiddling”技巧获得一些性能

具体来说,“查找整数对数基数 2”(最高位集的位置)的算法。这将让您确定哪些位比循环遍历每个位更直接。

如果您必须按从低到高的顺序处理位,您可以稍微修改 Kernighan 的方法来计算位:

/* note: untested code */
while (temp) {

    uint32_t bit = temp & (~(temp & (temp - 1)); /* isolate lowest bit */

    temp &= ~bit;

    uint32_t bit_number = /* use find log base 2 hack */;

    /* etc... */

}

这应该使 while 循环的迭代次数恰好等于设置的位数。您的原始循环将迭代次数等于最高设置位的位位置。

但是,如果这会产生任何可衡量的差异,我会感到惊讶,除非这是一段超级关键的代码。

【讨论】:

    【解决方案3】:

    使用标准库怎么样?无需移位或等...来测试位是否为真。对位集中的位的测试保证是恒定的时间。而且它写得更清晰,更容易理解。

    const std::bitset<32> oldbits( oldBuffer );
    const std::bitset<32> newbits ( newBuffer );
    
    for( size_t index = 0; index != oldbits.size(); ++index ) {
       if( oldbits[ index ] != newbits[ index ] ) {
           processNumber( index, newbits[ index ] )
       }
    }
    

    注意:这里也不需要 XOR,因为您可以直接访问这些位。不过,您可以使用它来节省性能。

    【讨论】:

      【解决方案4】:
       uint32_t temp = oldBuffer ^ newBuffer;
       uint32_t number = 0;
       uint32_t bitmask=1;
       while (temp != 0)
       {
           if (temp & 0x1)
           {
               processNumber(number, ((newBuffer & bitmask) != 0));
           }
           number++;
           temp = temp >> 1;
           bitmask <<=1;
       }
       oldBuffer = newBuffer;
      

      2个超级小改动...

      你的代码已经很高效了

      【讨论】:

        【解决方案5】:

        这取决于您期望 (oldBuffer ^ newBuffer) 的分布。如果它是完全随机的并且是全范围的 32 位,那么你平均有 16 个循环。

        一种可能的解决方案是制作这样的表格

        int lookup[255][8] = {
          { -1, -1, -1, -1, -1, -1, -1, -1 }, // 0 has no bits set
          {  0, -1, -1, -1, -1, -1, -1, -1 }, // 1 has only the 0th bit set
          {  1, -1, -1, -1, -1, -1, -1, -1 }, // 2 has only the 1st bit set
          {  0,  1, -1, -1, -1, -1, -1, -1 }, // 3 has the 0th, 1st bit set
          {  2, -1, -1, -1, -1, -1, -1, -1 }, // 4 has only the 2nd bit set
          ...
          {  0,  1,  2,  3,  4,  5,  6,  7 }, // 255 has all bits set
        }       
        

        这样,您必须循环 4 次(每个字节 1 次),然后每个设置的位循环 1 次(平均 4 次)- 嘿,这是 16。

        但是如果设置的位数很低(平均远低于 32 位的一半),那么查表就会失败。

        不幸的是,每次使用查表时都会增加一个乘法和加法,所以不一定好。你必须测试它。换句话说,它在常数时间内找到设置的位,但常数可能大于循环。这取决于您期望有多少设置位。

        【讨论】:

        • 绝对是一种有趣的方法。现在也许我会在它上面写一些代码来生成查找表,因为我不会成为填充中间点的人:)
        • 肯定——生成表格。这不仅乏味,而且容易出错。
        • 正要推荐这个...不错的一个
        【解决方案6】:

        你可以像递归 B-Tree 一样:)

        go(oldBuffer ^ newBuffer, 16, 0, newBuffer);
        ...
        
        void go(temp, half, pos, bitValue)
        {
            if (half > 1) {
              uint32_t mask = (1 << half) - 1;
              if (temp & mask)    go(temp & mask, half/2, pos, bitValue & mask);
              temp >>= half;
              if (temp & mask)    go(temp & mask, half/2, pos + half, (bitValue >> half) & mask);
            } else {
              if (temp & 1) processNumber(pos, bitValue&1);
              if (temp & 2) processNumber(pos+1, bitValue/2&1);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-02-21
          • 2016-04-28
          • 1970-01-01
          • 2020-10-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多