如何有效地检查位掩码?
How to efficiently check against bitmask?
我正在使用 inotify 并希望有效地检查报告的位掩码事件(参见 inotify man page)。
现在我可以粗暴地检查每个事件的每一点,但如果不是愚蠢的话,那将是非常粗鲁的,因为我每次都会有 N 个条件。或者正在打电话
( bitmask & mask ) == mask
for each mask 已经超级有效了吗?
由于生成的位掩码基本上只是一个明确定义的数字,我应该能够为此使用基本的算术运算。但在我自己想出一些东西之前,我想问一下是否有一种众所周知的、有效的方法来检查给定的位掩码。那么,有没有?
如果需要检查每个位掩码,除了显式检查之外别无他法。但是,如果已知具体的位掩码,则可以进行按位检查,从而有效地排除每一步中一半可能的位掩码。
如果你想检查一个位掩码,那么
if ((value & mask) == mask)
将为您提供完全匹配 ("all bits in the mask"),而
if ((value & mask) != 0)
将提供松散匹配 ("any bit in the mask")。编译器将进一步优化对零的检查。
如果你有多个位掩码,你想从时域中的每个检查中提取最大信息(极端情况:如果你得到的所有值都是肯定奇数, 你根本不需要检查第 0 位。它总是 1)。理想情况下,您需要确定第一轮有 50% 概率为 1 的位。
然后在这两个组中,您以相同的机会确定一个子组(在这两种情况下可能不相同)。
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 个状态,每个都映射到一位,并且每次调用只能设置一位 - 最简单的情况 - "serial" 序列将是 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 体系结构、分支预测等的考虑可能会胜过您可能尝试进行的任何优化。
除非你有一个非常复杂的设置,或一些其他限制(例如嵌入式设备),否则我担心分析、构建、调试和维护一个"optimized" 与 "brute force" check 相比,check 可能会平衡你可以从前者中挤出的任何优势。
为了检查掩码的多个位,我使用了一个循环。如果您使用的是一个不错的编译器,它应该相当不错地优化代码。除非您有严重的性能问题,否则不值得手动优化,因为我所知道的所有 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;
}
}
}
如果只设置了一个位并且您的代码不需要可移植,则可以使用内在函数为您提供设置位的位置,然后在 switch 语句中使用结果。
对于 gcc,例如成为
__builtin_ffs
我正在使用 inotify 并希望有效地检查报告的位掩码事件(参见 inotify man page)。
现在我可以粗暴地检查每个事件的每一点,但如果不是愚蠢的话,那将是非常粗鲁的,因为我每次都会有 N 个条件。或者正在打电话
( bitmask & mask ) == mask
for each mask 已经超级有效了吗?
由于生成的位掩码基本上只是一个明确定义的数字,我应该能够为此使用基本的算术运算。但在我自己想出一些东西之前,我想问一下是否有一种众所周知的、有效的方法来检查给定的位掩码。那么,有没有?
如果需要检查每个位掩码,除了显式检查之外别无他法。但是,如果已知具体的位掩码,则可以进行按位检查,从而有效地排除每一步中一半可能的位掩码。
如果你想检查一个位掩码,那么
if ((value & mask) == mask)
将为您提供完全匹配 ("all bits in the mask"),而
if ((value & mask) != 0)
将提供松散匹配 ("any bit in the mask")。编译器将进一步优化对零的检查。
如果你有多个位掩码,你想从时域中的每个检查中提取最大信息(极端情况:如果你得到的所有值都是肯定奇数, 你根本不需要检查第 0 位。它总是 1)。理想情况下,您需要确定第一轮有 50% 概率为 1 的位。
然后在这两个组中,您以相同的机会确定一个子组(在这两种情况下可能不相同)。
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 个状态,每个都映射到一位,并且每次调用只能设置一位 - 最简单的情况 - "serial" 序列将是 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 体系结构、分支预测等的考虑可能会胜过您可能尝试进行的任何优化。
除非你有一个非常复杂的设置,或一些其他限制(例如嵌入式设备),否则我担心分析、构建、调试和维护一个"optimized" 与 "brute force" check 相比,check 可能会平衡你可以从前者中挤出的任何优势。
为了检查掩码的多个位,我使用了一个循环。如果您使用的是一个不错的编译器,它应该相当不错地优化代码。除非您有严重的性能问题,否则不值得手动优化,因为我所知道的所有 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;
}
}
}
如果只设置了一个位并且您的代码不需要可移植,则可以使用内在函数为您提供设置位的位置,然后在 switch 语句中使用结果。 对于 gcc,例如成为
__builtin_ffs