rol*_*vax 171 c++ optimization integer range compiler-optimization
我正在使用一种int类型来存储一个值.根据程序的语义,值总是在很小的范围内变化(0 - 36),并且仅使用int(不是a char)因为CPU效率.
似乎可以在如此小的整数范围内执行许多特殊的算术优化.可以将对这些整数的许多函数调用优化为一小组"神奇"操作,并且甚至可以将某些函数优化为表查找.
那么,是否有可能告诉编译器这int总是在那么小的范围内,并且编译器是否可以进行这些优化?
小智 227
对的,这是可能的.例如,gcc您可以使用__builtin_unreachable告诉编译器有关不可能的条件,如下所示:
if (value < 0 || value > 36) __builtin_unreachable();
Run Code Online (Sandbox Code Playgroud)
我们可以在宏中包装上面的条件:
#define assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
Run Code Online (Sandbox Code Playgroud)
并像这样使用它:
assume(x >= 0 && x <= 10);
Run Code Online (Sandbox Code Playgroud)
如您所见,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;
}
}
Run Code Online (Sandbox Code Playgroud)
生产:
func(int):
mov eax, 17
ret
Run Code Online (Sandbox Code Playgroud)
然而,一个缺点是,如果您的代码打破了这样的假设,那么您将得到未定义的行为.
即使在调试版本中,它也不会在发生这种情况时通知您.要更容易地使用假设来调试/测试/捕获错误,您可以使用混合的假设/断言宏(@David Z的信用),如下所示:
#if defined(NDEBUG)
#define assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
#else
#include <cassert>
#define assume(cond) assert(cond)
#endif
Run Code Online (Sandbox Code Playgroud)
在调试版本(具有NDEBUG 不定义),它的工作原理像一个普通的assert,印刷错误消息和abort"ING程序,并在发布版本它利用一个假设的,产生优化代码.
但请注意,它不能代替常规assert- cond仍然在发布版本中,所以你不应该做类似的事情assume(VeryExpensiveComputation()).
Lun*_*din 60
有标准的支持.你应该做的是包括stdint.h(cstdint)然后使用类型uint_fast8_t.
这告诉编译器您只使用0到255之间的数字,但是如果它提供更快的代码则可以使用更大的类型.类似地,编译器可以假设变量永远不会有大于255的值,然后相应地进行优化.
当您确切知道范围是什么时,当前答案是有利的,但如果您仍然希望在值超出预期范围时仍然需要正确的行为,那么它将无效.
对于这种情况,我发现这种技术可以工作:
if (x == c) // assume c is a constant
{
foo(x);
}
else
{
foo(x);
}
Run Code Online (Sandbox Code Playgroud)
这个想法是代码数据权衡:你将1位数据(无论是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);
}
Run Code Online (Sandbox Code Playgroud)
只需使用-O3并注意预评估常数0x20和0x30e在汇编输出.
我只想说,如果你想要一个更标准的C++解决方案,你可以使用该[[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;
}
}
Run Code Online (Sandbox Code Playgroud)
其中你可以看到,结果几乎相同的代码:
detail::unreachable():
rep ret
func(int):
movl $17, %eax
ret
Run Code Online (Sandbox Code Playgroud)
当然,缺点是你得到一个[[noreturn]]功能确实会返回的警告.
| 归档时间: |
|
| 查看次数: |
6910 次 |
| 最近记录: |