bits twiddling hack:每n位删除一位的最有效方法是什么?

时间:2014-01-15 15:44:49

标签: c++ c algorithm c++11 bit-manipulation

这是我的问题:

Bits twiddling hack

我需要在CC++11中非常有效地完成此操作(我需要在超级计算机上执行此操作数十亿次)。 Nn在编译时已知(模板参数)。最有效的算法是什么?

以下是一个例子:

#include <iostream>
#include <climits>
#include <type_traits>
#include <bitset>

template <unsigned int Modulo,
          typename Type,
          unsigned int Size = sizeof(Type)*CHAR_BIT,
          class = typename std::enable_if<std::is_integral<Type>::value
                                       && std::is_unsigned<Type>::value>::type>
inline Type f(Type x)
{
    // The most inefficient algorithm ever
    std::bitset<Size> bx(x);
    std::bitset<Size> by(0);
    unsigned int j = 0;
    for (unsigned int i = 0; i < Size; ++i) {
        if (i%Modulo) {
            by[j++] = bx[i];
        }
    }
    return by.to_ullong();
}

int main()
{
    std::bitset<64> x = 823934823;
    std::cout<<x<<std::endl;
    std::cout<<(std::bitset<64>(f<2>(x.to_ullong())))<<std::endl;
    return 0;
}

1 个答案:

答案 0 :(得分:2)

首先是语义......

语义上(从概念上讲,因为你实际上不能在这里使用迭代器),你正在做一个std::copy_if,你的输入和输出范围是std::bitset<N>,你的谓词是表格的lambda (使用C ++ 14通用lambda表示法)

[](auto elem) { return elem % n != 0; }

此算法在分配数量和谓词调用次数方面具有O(N)的复杂性。因为std::bitset<N>没有迭代器,所以必须逐位检查。这意味着带有手写谓词的循环与假设的可迭代std::copy_if上的std::bitset<N>完全相同。

这意味着就渐远效率而言,您的算法不应被视为低效

...优化最后

因此,如果您的算法没有像二次复杂度那样做任何不好的结论,那么它的常数因子能够被优化吗? std::bitset效率的主要来源是您的硬件可以并行处理多个(8,16,32或64)位。如果您有权访问该实现,则可以编写自己的copy_if来利用该并行性,例如通过特殊的硬件指令,查找表或一些bit-twiddling algorithm

E.g。这是成员函数count()以及gcc和SGI扩展Find_first_()Find_next_()的实现方式。旧的SGI实现使用256个条目的查找表来处理每个8位char的位上的位计数和准迭代。最新的gcc版本使用__builtin_popcountll()__builtin_ctzll()为每个64位字进行填充计数和位查找。

不幸的是,std::bitset没有暴露其无符号整数的底层数组。因此,如果您想改进已发布的算法,则需要编写自己的BitSet类模板(可通过调整自己的标准库的来源)并为其提供成员函数copy_if(或类似)利用你的硬件。与当前算法相比,它可以提高8到64倍的效率。