无分支优化:判断无符号整数是否属于两个编译时常量区间
无符号整数双区间归属检查的无分支优化方案需求
在大量场景中,我需要判断给定无符号整数是否属于两个不重叠区间之一,但这类检查往往存在明显性能损耗。我的实现优先级从高到低为:
- 消除数据相关分支
- 最小化总操作数
- 缩短依赖链(因关注吞吐量,该优先级最低)
多数场景下无符号整数为32位(例如用整数运算模拟float操作),为便于研究与阐述,我选用C++的uint8_t类型展开分析,以此规避RISC处理器上整数常量高效构造等额外问题。
通过对比编译输出与手动优化示例,发现现代编译器通常能为可通过AND处理的场景生成最优代码(如下方代码示例的case 2、3),但无法处理依赖XOR将两个区间转换为一个区间的场景(如case 0、1)。也可基于OR构建优化方案(如case 4),但这类方案总有对应的AND镜像实现。
我虽能手动推导部分场景,但缺乏此类优化的通用方案,也不清楚编译器的处理逻辑:是针对双边范围检查组合使用专用转换,还是仅依赖整数转换的通用简化?我查阅了Warren的《Hacker's Delight(第二版)》、技术问答社区及学术文献,暂未找到通用方法。
理想方案要求:
给定两个由编译时常量上下界定义的区间,提供一种算法判断是否可通过AND、XOR(或其他操作,如假设单周期MUL)优化归属检查;若可行,给出具体操作序列与常量。
测试代码示例
#include <cstdio> #include <cstdlib> #include <cstdint> #define CASE (0) #if (CASE == 0) bool in_range_1 (uint8_t a) { return ((a >= 0x40u) && (a < 0x50u)) || ((a >= 0x54u) && (a < 0x58u)); } bool in_range_2 (uint8_t a) { return ((a ^ 0x44u) < 0x14u); } #elif (CASE == 1) bool in_range_1 (uint8_t a) { return ((a >= 0x67u) && (a < 0x70u)) || ((a >= 0x90u) && (a < 0x98u)); } bool in_range_2 (uint8_t a) { return (((a ^ 0x10u) - 0x77u) < 0x11u); } #elif (CASE == 2) bool in_range_1 (uint8_t a) { return ((a >= 0x10u) && (a < 0x20u)) || ((a >= 0x30u) && (a < 0x40u)); } bool in_range_2 (uint8_t a) { return ((a & 0xd0u) == 0x10u); } #elif (CASE == 3) bool in_range_1 (uint8_t a) { return ((a >= 0x01u) && (a < 0x21u)) || ((a >= 0x41u) && (a < 0x61u)); } bool in_range_2 (uint8_t a) { return (((a & 0xbfu) - 0x01u) < 0x20u); } #elif (CASE == 4) bool in_range_1 (uint8_t a) { return ((a >= 0x40u) && (a < 0x50u)) || ((a >= 0x60u) && (a < 0x70u)); } bool in_range_2 (uint8_t a) { return ((a | 0x2fu) == 0x6fu); // could also be: ((a & 0xd0u) == 0x40u) } #else #error no such CASE #endif // CASE int main (void) { uint8_t a = 0; do { bool ref = in_range_1 (a); bool res = in_range_2 (a); if (res != ref) { printf ("error: a=%02x res=%d ref=%d\n", a, res, ref); return EXIT_FAILURE; } a++; } while (a); printf ("case %d: test passed\n", CASE); return EXIT_SUCCESS; }
优化效果示例(Case 0)
使用Clang 20.1 -O3为32位RISC-V平台编译Case 0,功能相同的两个函数生成的汇编代码如下:
in_range_1(unsigned char): andi a1, a0, 240 andi a0, a0, 252 addi a1, a1, -64 addi a0, a0, -84 seqz a1, a1 seqz a0, a0 or a0, a0, a1 ret in_range_2(unsigned char): xori a0, a0, 68 sltiu a0, a0, 20 ret
内容的提问来源于stack exchange,提问作者njuffa
相关产品推荐
相关产品推荐

