如何有效地检查位掩码?

时间:2015-03-05 12:23:56

标签: c++ c bit bitmask

我正在使用inotify并希望有效地检查报告的位掩码事件(请参阅inotify man page)。

现在我可以粗暴地检查每一个事件的每一点,但如果不是愚蠢的话,这将非常粗糙,因为我每次都会有N个条件。或者正在呼叫

( bitmask & mask ) == mask

每个掩码已经超级高效?

由于结果位掩码基本上只是一个定义良好的数字,我应该可以使用基本的算术运算。但在我自己想到一些事情之前,我想问一下是否有一种众所周知的,有效的方法来检查给定的位掩码。那么,有吗?

4 个答案:

答案 0 :(得分:8)

如果要检查一个位掩码,那么

if ((value & mask) == mask)

将为您提供完全匹配(“掩码中的所有位”)和

if ((value & mask) != 0)

将提供一个松散的匹配(“掩码中的任何位”)。编译器将进一步优化对零的检查。

如果您有多个位掩码,您希望从时域中的每个检查中提取最大信息(极端情况:如果您获得的所有值肯定奇数,则不需要检查第0位。它总是1)。理想情况下,您需要识别第一轮比特,其概率为50%。

在两组中,您都可以识别出具有相同机会的子组(在两种情况下可能不同)。

if ((value & SPECIAL_MASK_1) == SPECIAL_MASK_1) {
    if ((value & SPECIAL_MASK_2) == SPECIAL_MASK_2) {
        ...
    } else {
        ...
    }
} else {
    if ((value & SPECIAL_MASK_3) == SPECIAL_MASK_3) {
        ...
    } else {
        ...
    }
}

如果你有32个状态,每个映射到一个位,每次调用只能设置一个位 - 最简单的情况 - “串行”序列将是一个接一个的32个检查之一

if ((mask & 0x00000001) == 0x00000001) {
} else if ((mask & 0x00000002) == 0x00000002) {
}
...

并且第一个简单的优化将首先对最频繁的事件进行检查。假设设置了第三位中的三个中的一个;你先检查第七位。

这样你最终只能在33%的时间内完成一次检查;然后可能有两次检查另外20%的时间,......,最后平均,你可能会运行七次检查。

另一种可能性是

if (mask & 0x0000FFFF) {
    // The bit is in the LSW
    if (mask & 0x0000FF00) {
        // MSB of LSW
        if (mask & 0x0000F000) {
            ...
        } else {
        }
    }
} else {
}

这将每次完成五次检查。然而,在那时,关于CPU架构,分支预测等的考虑可能胜过您可能尝试的任何优化。

除非您有非常复杂的设置或其他约束(例如嵌入式设备),否则我担心分析,构建,调试和维护“优化​​”与“暴力”的成本检查可能不仅仅是平衡你可以挤出前者的任何优势。

答案 1 :(得分:2)

为了检查掩码的多个位,我使用循环。如果你正在使用一个像样的编译器,它应该相当优化代码。除非您遇到严重的性能问题,否则不值得手动优化,因为我所知道的所有CPU都在单个指令中实现逻辑位测试或逐位AND操作。所以你有两个指令:每个位的逻辑指令和CPU分支条件指令。没有大量的代码可以运行 - 据我所知 - 是不可能的。 (请注意,由于mask是32位宽,如果您在16位核心CPU上运行,则会有更多指令来测试这两半。)

void processEvents(uint32_t events)
{
    uint32_t bitToTest;
    // Check each bit in turn
    for(bitToTest = 1; bitToTest < events; bitToTest << 1)
    {
        // Check which bit is set.  If none then the default case is used.
        switch(bitToTest & events)
        {
            case IN_ACCESS:
                // Handle the IN_ACCESS event flag here.
                break;
            case IN_ATTRIB:
                // Handle the IN_ATTRIB event flag here.
                break;
            // Et cetera...
            default:
                // No flag was set, so do nothing.
                break;
        }
    }
}

答案 2 :(得分:2)

如果只设置了一个位,并且您的代码不必是可移植的,则可以使用提供设置位位置的内在函数,然后在switch语句中使用结果。 对于gcc,例如是

__builtin_ffs

答案 3 :(得分:1)

如果需要检查每个位掩码,除了明确检查之外别无他法。但是,如果已知特定的位掩码,则可以进行逐位检查,从而有效地导致在每一步中排除一半可能的位掩码。