如果你试图微调布尔运算,要么是过早优化了,要么是在大量进行布尔数据操作。对于前者,答案是不要这样做;对于后者,你可能问错了问题。如果真正的问题是如何优化(许多)布尔数据的(许多)操作,则答案是使用基于“标志”(即使用更好的算法)的替代表示。这将允许你将更多的数据可移植地和易读地适配到缓存中,并同时执行多个操作和测试。
为什么/如何更好?
缓存
考虑一个缓存行大小为64字节的系统。64个
_Bool
可以适配到数据缓存行中,而8倍于此的数据可以适配到其中。您也可能会有更小的指令代码——从1个附加指令到少32倍的指令。这在紧密循环中可能会产生很大的差异。
操作
大多数操作涉及一个或两个(通常非常快速)操作和单个测试,而无论您要测试多少个标志。由于这可以同时包含多个值,因此每个操作可以执行(通常是32或64倍)更多的工作。
分支
由于可以同时完成多个操作和测试,原本可能有32(或64)个可能的分支可以减少到一个。这可以减少分支预测错误。
可读性
通过使用一个名为掩码常量的良好命名的掩码,复杂嵌套的
if-else-if-else
块可以减少为单个易读的行。
可移植性
_Bool
在早期版本的C中不可用,C++使用不同的机制来表示布尔类型;但是,标志在较旧的C版本中也适用,并且与C++兼容。
下面是如何使用标志设置掩码的实际示例:
int isconsonant(int c){
const unsigned consonant_mask = (1<<('b'-'a'))|
(1<<('c'-'a'))|(1<<('d'-'a'))|(1<<('f'-'a'))|(1<<('g'-'a'))|
(1<<('h'-'a'))|(1<<('j'-'a'))|(1<<('k'-'a'))|(1<<('l'-'a'))|
(1<<('m'-'a'))|(1<<('n'-'a'))|(1<<('p'-'a'))|(1<<('q'-'a'))|
(1<<('r'-'a'))|(1<<('s'-'a'))|(1<<('t'-'a'))|(1<<('v'-'a'))|
(1<<('w'-'a'))|(1<<('x'-'a'))|(1<<('y'-'a'))|(1<<('z'-'a'));
unsigned x = (c|32)-'a'; // ~ tolower
int ret = (x<32)<<(x&31);
return ret & consonant_mask;
}
//compiles to 7 operations to check for 52 different values
isconsonant:
or edi, 32 # tmp95,
xor eax, eax # tmp97
lea ecx, [rdi-97] # x,
cmp ecx, 31 # x,
setbe al #, tmp97
sal eax, cl # ret, x
and eax, 66043630 # tmp96,
ret
这个概念可以用于同时操作一个模拟的布尔值数组,例如:
_Bool isSpecificMaskSet(uint32_t x, uint32_t m){
return x==m;
}
_Bool isLimitedMaskSet(uint32_t x, uint32_t m, uint32_t v){
return (x&m) == v;
}
_Bool isNoMaskBitSet(uint32_t x, uint32_t m){
return (x&m) == 0;
}
_Bool areAllMaskBitsSet(uint32_t x, uint32_t m){
return (x&m) == m;
}
uint32_t setMaskBits(uint32_t x, uint32_t m){
return x|m;
}
uint32_t toggleMaskBits(uint32_t x, uint32_t m){
return x^m;
}
uint32_t clearMaskBits(uint32_t x, uint32_t m){
return x&~m;
}
uint32_t getMaskBits(uint32_t x, uint32_t m){
return x&m;
}
uint32_t getMaskBitsNotSet(uint32_t x, uint32_t m){
return (x&m)^m;
}
status ^=1;
这是一种方式。 (翻译说明:尽可能保持原文意思和语法结构,使用通用词汇来翻译代码,不添加解释或额外信息。) - user2736738xor value,8
来处理第三位的比特位。但是为什么需要一个函数来执行这样简单的操作呢? - Weather Vane+1
的示例视为一种位翻转,但会损坏上位位(通过执行+that_bit_value
可以翻转任何位,当您不关心上面的位被修改时)。但我认为没有单个指令SYSTEM V ABI函数仅翻转所需的位,因为参数在rdi/edi/...
中传入,并且结果在rax/eax/...
(和ret
!)中返回。但这是奇怪的要求,因为这不是您在实际生产中需要的东西,因此您试图实现的任何内容都是纯粹人工的,并且与真实世界的软件无关。 - Ped7g