【发布时间】:2017-05-12 13:23:45
【问题描述】:
我进行了一个简单的实验,将 if-else 与仅 if(预设默认值)进行比较。示例:
void test0(char c, int *x) {
*x = 0;
if (c == 99) {
*x = 15;
}
}
void test1(char c, int *x) {
if (c == 99) {
*x = 15;
} else {
*x = 0;
}
}
对于上面的函数,我得到了完全相同的汇编代码(使用cmovne)。
但是当添加一个额外的变量时:
void test2(char c, int *x, int *y) {
*x = 0;
*y = 0;
if (c == 99) {
*x = 15;
*y = 21;
}
}
void test3(char c, int *x, int *y) {
if (c == 99) {
*x = 15;
*y = 21;
} else {
*x = 0;
*y = 0;
}
}
组装突然变得不同:
test2(char, int*, int*):
cmp dil, 99
mov DWORD PTR [rsi], 0
mov DWORD PTR [rdx], 0
je .L10
rep ret
.L10:
mov DWORD PTR [rsi], 15
mov DWORD PTR [rdx], 21
ret
test3(char, int*, int*):
cmp dil, 99
je .L14
mov DWORD PTR [rsi], 0
mov DWORD PTR [rdx], 0
ret
.L14:
mov DWORD PTR [rsi], 15
mov DWORD PTR [rdx], 21
ret
似乎唯一的区别是顶部的movs 是在je 之前还是之后完成的。
现在(对不起,我的组装有点粗糙),在跳转后使用movs 不是总是更好,以节省管道冲洗吗?如果是这样,为什么优化器 (gcc6.2 -O3) 不使用更好的方法?
【问题讨论】:
-
我预测这个问题会有很多人赞成 :)
-
如果你想知道 X 是否比 Y 快,你应该分析,profile 和 profile。只有准确的时间安排才能解决问题。
-
原因可能是因为这两个指针可能指向同一个对象。例如,如果它是一个内存映射的输出寄存器,那么两段代码将产生截然不同的结果。
-
@StoryTeller:它们不是
volatile,因此允许编译器假设它不必在*x = 0存储c==99。代码差异是由于编译器从字面上实现代码,因为缺少任何其他提示。通过配置文件引导的优化,它会知道 if() 条件被采用/不采用的可能性,并相应地布置代码,因此最常见的情况是快速路径。 (Or withlikely/unlikelymacros using GNU C__builtin_expect()) -
@StoryTeller:是的,潜在的别名意味着编译器需要按程序顺序执行最后一组存储。但是两个分支都通过两个指针存储,所以它并没有真正减少优化的可能性。如果有两个间接级别,第一个存储可以更改第二个存储的目的地,那将是另一回事。但是由于
*x不能别名y,只能别名*y,我同意添加restrict可能在实践或理论上都没有关系。
标签: c++ c assembly optimization