我可以通过给出一个整数的范围来提示优化器吗?

我正在使用一个inttypes来存储一个值。 通过程序的语义,值总是在一个非常小的范围内变化(0-36), int (不是char )只是因为CPU的效率。

似乎很多特殊的算术优化可以在如此小范围的整数上执行。 对这些整数的许多函数调用可能被优化成一小组“神奇”的操作,并且一些函数甚至可以优化成查表。

那么,是否有可能告诉编译器这个int总是在这个小范围内,编译器是否有可能做这些优化?

对的,这是可能的。 例如,对于gcc你可以使用__builtin_unreachable告诉编译器关于不可能的条件,如下所示:

 if (value < 0 || value > 36) __builtin_unreachable(); 

我们可以用macros来包装上面的条件:

 #define assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) 

像这样使用它:

 assume(x >= 0 && x <= 10); 

正如你所看到的 , gcc根据这些信息执行优化:

 #define assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) int func(int x){ assume(x >=0 && x <= 10); if (x > 11){ return 2; } else{ return 17; } } 

生产:

 func(int): mov eax, 17 ret 

但是,一个缺点是, 如果你的代码违反了这样的假设,你会得到一个未定义的行为

即使在debugging版本中,它也不会在这种情况发生时通知您。 要更容易地使用假设来debugging/testing/捕获错误,可以使用混合假设/断言macros(信用于@David Z),如下所示:

 #if defined(NDEBUG) #define assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) #else #include <cassert> #define assume(cond) assert(cond) #endif 

在debugging版本( NDEBUG未定义)中,它像一个普通的assert ,打印错误消息和abort程序,在发布版本中使用假设,生成优化的代码。

但是,请注意,它不是常规assert的替代品 – cond仍然在发布版本中,所以您不应该这样做assume(VeryExpensiveComputation())

有这个标准的支持。 你应该做的是包括stdint.hcstdint ),然后使用typesuint_fast8_t

这告诉编译器,你只使用0到255之间的数字,但是如果给出更快的代码,则可以使用更大的types。 同样,编译器可以假设variables永远不会超过255的值,然后做相应的优化。

当前的答案对于确实知道范围的情况是有利的,但是如果在超出预期范围的情况下仍然需要正确的行为,那么它将不起作用。

对于这种情况,我发现这种技术可以工作:

 if (x == c) // assume c is a constant { foo(x); } else { foo(x); } 

这个想法是一个代码数据权衡:你将1位数据 (不pipe是x == c )移动到控制逻辑中
这暗示了优化器x实际上是一个已知的常量c ,鼓励它将foo的第一次调用与其他内容分开并进行优化,可能相当严重。

确保实际上将代码分解到单个子程序foo ,但不要复制代码。

例:

为了使这个技巧起作用,你需要有点幸运 – 有些情况下编译器决定不静态地评估事物,而且它们是任意的。 但是,当它工作时,它运作良好:

 #include <math.h> #include <stdio.h> unsigned foo(unsigned x) { return x * (x + 1); } unsigned bar(unsigned x) { return foo(x + 1) + foo(2 * x); } int main() { unsigned x; scanf("%u", &x); unsigned r; if (x == 1) { r = bar(bar(x)); } else if (x == 0) { r = bar(bar(x)); } else { r = bar(x + 1); } printf("%#x\n", r); } 

只需使用-O3 ,并在汇编器输出中注意预先计算的常量0x200x30e

我只是想说,如果你可能想要一个更标准的C ++解决scheme,你可以使用[[noreturn]]属性写你自己的unreachable

所以我会重新为deniss举例说明:

 namespace detail { [[noreturn]] void unreachable(){} } #define assume(cond) do { if (!(cond)) detail::unreachable(); } while (0) int func(int x){ assume(x >=0 && x <= 10); if (x > 11){ return 2; } else{ return 17; } } 

正如你所看到的 ,结果几乎相同的代码:

 detail::unreachable(): rep ret func(int): movl $17, %eax ret 

不利的一面是,你会得到一个[[noreturn]]函数确实会返回的警告。