C++位掩码/条件分支优化

C++ bitmask / conditional branch optimization

本文关键字:分支 优化 条件 掩码 C++      更新时间:2023-10-16

我正在尝试减少下面显示的if语句(第二块代码)的执行时间。 它涉及一个位掩码,其中掩码数组包含 8 个用作掩码的整数,并设置如下:

static unsigned int masks[8];
void setupMasks() {
    int mask = 3; // 0000 0000 0000 0000 0000 0000 0000 0011
    for(unsigned int i=0; i < 8; i++) {
        masks[i] = (mask << (i * 4));
    }
}

下面 testarr 中的每个整数实际上包含 8 个结果。 每个结果是 32 位 int 的 4 位,我只想知道 4 位中的下两位是否有任何位是 1。 下面的代码在另一个更新结果的 for 循环中执行。 失败计数是一个本地定义的 int 数组。 我想避免屏蔽,但 testarr 中的数据来自我无法更改的 API。 无论如何,我认为 if 语句比掩蔽花费更多的时间,但我可能是错的。 有没有人看到优化的方法?

for(unsigned int i = 0; i < 8 && dumped < numtodump; i++, dumped++) { //8 results per 32-bit value
    unsigned int fails = 0;
    for(unsigned int j = 0; j < 32; j++) {
    if((testarr[j * numintsperpin + resultnum] & masks[i]) && failcount[j]++ <= 10000) { //have a fail
            failingpins[fails++] = &pins[j];
        }
    }
}

对不起,如果我之前的帖子不清楚。 以下是完整功能。我试图尽可能简化问题陈述。 抱歉,如果我遗漏了有用的细节。

void process(vector<int> &testarr, vector<unsigned int> &failcount, vector<pin> &pins, vector<unsigned int> &muxaddr, unsigned int base, StopWatch &acc1) {
    unsigned int labeloffset = 400;
    unsigned int startindex = 50;
    unsigned int numtodump = 1000;
    unsigned int numintsperpin = testarr.size() / pins.size();
    pin** failingpins = new pin*[32];
    acc1.start();
    int count = 0;
    unsigned int dumped = 0;
    unsigned int resultnum = 0;
    while(dumped < numtodump) {
        for(unsigned int i = 0; i < 8 && dumped < numtodump; i++, dumped++) { //8 results per 32-bit value
            unsigned int currentindex = labeloffset + dumped + startindex;
            unsigned int fails = 0;
            for(unsigned int j = 0; j < pins.size(); j++) {
                if((testarr[j * numintsperpin + resultnum] & masks[i]) && failcount[j]++ <= 10000) { //have a fail
                    failingpins[fails++] = &pins[j];
                }
            }
            unsigned int saddr = muxaddr[currentindex];
            if(fails > 0) {             
                logFails(fails, muxaddr[currentindex] - base, failingpins);
            }
        }
        resultnum++;
    }
    acc1.accumulate();  
}

看看我是否有这个权利:

testarr 中的每个条目都是一个 32 位值,包含 8 x 4 位字段

您想知道是否有任何字段设置了较低的 2 位中的任何一个,即您希望用以下方法屏蔽每个 32 位值:

0011 0011 0011 0011 0011 0011 0011 0011

那为什么不呢:

for( int i=0; i<testarr_length; i++ )
   if( testarr[i] & 0x33333333 )
      // do something !

如果您需要知道设置了多少个字段,则

for( int i=0; i<testarr_length; i++ )
{
   unsigned int dword= testarr[i];
   for( int field=0; field<8; field++ )
   {
        if( dword & 0x3 )
            // do something
        dword= dword >> 4;
   }     
}

您可以尝试以下操作

inline int count(int x)
{
    static int mask1 = 0x11111111;
    static int mask2 = 0x22222222;
    return __builtin_popcount(x & mask1 | x & mask2 << 1);
}
// ...
unsigned int fails = 0;
for(unsigned int j = 0; j < 32; j++) {
    int c = count(testarr[j * numintsperpin + resultnum]);
    if(c && (failcount[j]+=c) <= 10000) { //have a fail
        failingcols[fails+=c] = &column[j];
    }
}

其中我将掩码拆分为两个单独的掩码,并使用了函数__builtin_popcount,该函数仅在一个 CPU 操作中计算给定整数的位数,从而完全避免了i循环。

__builtin_popcount应该由编译器提供,例如,上面的示例适用于带有选项 -msse4.2 的 Clang 和 GCC。据我所知,MS编译器分别提供了函数__popcnt

我不知道dumped的作用是什么,但它没有显示在你的循环中,所以我只是忽略了它。

编辑

我现在看到了更新的问题,除了失败的数量之外,dumped似乎在重新编码失败的实际位置方面发挥着重要作用。在这种情况下,我的解决方案不适用。这个新问题更难优化。