设置/清除位:bitshift或bitmask lookup?

时间:2015-11-13 23:14:05

标签: c++ bit-manipulation bit-shift chess

我正在使用基于位板的国际象棋引擎,其中一项行动是在无符号的64位整数中设置/清除位。因为我不熟悉哪些代码会运行得更快'在某些处理器上,这是我无法解决的问题。

设置和清除位是一个非常简单的操作,但我应该使用(用于设置):

uint64_t bitboard |= 1ULL << index;

或:

uint64_t bitboard |= BITMASK[index];

其中BITMASK[]是一些预先计算的整数数组,其中只有一位(index)被设置。

乍一看,位移似乎是明显更快的选择,因为位移总是比内存查找更快。

但是在国际象棋引擎的上下文中,可能会大量执行此操作,将查找表存储在处理器的高速缓存中是有意义的,这可能会使查找速度更快表。或者是吗?

此外,它甚至有所作为吗?

或许可能是一个愚蠢的考虑因素,但要知道它并不会有什么坏处。

2 个答案:

答案 0 :(得分:3)

与表查找相比,shift方法应该更快,因为它避免了额外的内存引用。但出于教育目的,基准测试会很有趣。

答案 1 :(得分:2)

我很快就掀起了这个(非常粗暴,赦免)的功能:

#include <iostream>
#include <random> // std::mt19937()

typedef unsigned long long uint64;

uint64 SET_BITMASK[64];

void init_bitmask()
{
    for(int i = 0; i < 64; i++) SET_BITMASK[i] = 1ULL << i;
}

int main()
{
    std::mt19937 gen_rand(42);
    uint64 bb = 0ULL;
    double avg1, avg2;

    init_bitmask();

    for(unsigned int i = 0; i < 10; i++)
    {
        std::clock_t begin = std::clock();

        for(unsigned int j = 0; j < 99999999; j++)
        {
            bb |= 1ULL << (gen_rand() % 64);
        }

        std::clock_t end = std::clock();

        std::cout << "For bitshifts, it took: " << (double) (end - begin) / CLOCKS_PER_SEC << "s." << std::endl;
        avg1 += (double) (end - begin) / CLOCKS_PER_SEC;

        bb = 0ULL;

        begin = std::clock();

        for(unsigned int j = 0; j < 99999999; j++)
        {
            bb |= SET_BITMASK[gen_rand() % 64];
        }

        end = std::clock();

        std::cout << "For lookups, it took: " << (double) (end - begin) / CLOCKS_PER_SEC << "s." << std::endl << std::endl;
        avg2 += (double) (end - begin) / CLOCKS_PER_SEC;
    }

    std::cout << std::endl << std::endl << std::endl;

    std::cout << "For bitshifts, the average is: " << avg1 / 10 << "s." << std::endl;
    std::cout << "For lookups, the average is: " << avg2 / 10 << "s." << std::endl;
    std::cout << "Lookups are faster by " << (((avg1 / 10) - (avg2 / 10)) / (avg2 / 10))*100 << "%." << std::endl;
}

对于每次迭代,平均十个超过一亿个位集对于位移是1.61603s,对于一致的查找是1.57592s(即使对于不同的种子值)。

查找表格看起来惊人地大致2.5%(在此特定用例中)。

注意: 我使用随机数来防止任何不一致,如下所示。

如果我使用i % 64进行移位/索引,则比特移位速度提高约6%

如果我使用常数来移动/索引,输出变化大约8%,介于-4%和4%之间,这让我觉得一些有趣的猜测业务正在发挥作用。要么是,要么平均为0%;)

我无法得出结论,因为这肯定不是一个真实的场景,因为即使在国际象棋引擎中,这些设置的位案例也不会快速连续地相互跟随。我只能说,差异可能微不足道。我还可以补充一点,查找表是不一致的,因为您是否已经缓存了表格。我个人会在我的引擎中使用bithifts。