我有以下两个功能基本相同:
enum Direction{
N = 0,
NW,
W,
SW,
S,
SE,
E,
NE,
TOTAL_DIRS
};
char const * const strings[] = {"N", "NW", "W", "SW", "S", "SE", "E", "NE"};
char const *
getDirString2(unsigned dir) {
if (TOTAL_DIRS > dir)
return strings[dir];
return nullptr;
}
char const *
getDirString3(unsigned dir) {
char const * const strings[] = {"N", "NW", "W", "SW", "S", "SE", "E", "NE"};
if (TOTAL_DIRS > dir)
return strings[dir];
return nullptr;
}
但是,尽管g ++像我期望的那样优化了使用全局数组的函数。它为替代方案创建了更多复杂的代码。 Clang为两者创建相同的代码,如果我改用switch语句,则clang和c ++也会创建与getDirString2
相同的代码。
这里是编译器资源管理器https://godbolt.org/z/GxvrTv
的链接这是我应该为g ++提交错误报告的东西吗?还是有充分的理由呢?
答案 0 :(得分:3)
我想您可以称这为错过的优化,尽管这对gcc家伙来说有点苛刻。
gcc在编译getDirString3
时,完全按照您的要求进行操作-在堆栈上构造一个字符串数组,然后仅返回其中的一个元素。
clang看到此数组从不更改,而是将其构造在静态存储中,请参见:https://godbolt.org/z/24n-N7
要使gcc像clang一样生成代码,请将getDirString3
中的数组声明为static
(首先是个好主意),请参见:https://godbolt.org/z/henD2Z >
答案 1 :(得分:0)
注意:首先,我说这是不符合标准的优化,但是在评论中与Chris Dodd进行讨论之后,我意识到这在一定程度上是不正确的。>
更新后的答案:
我应该为此提交g ++错误报告吗?还是有充分的理由?
clang和gcc都可以进行相同的优化,但是默认情况下在gcc中禁用了它。因此,这不是错误,并且有原因。
c做了什么?
在您的示例中,编译器看到本地数组是常量,因此,每次调用该函数时都不需要在堆栈上构造它。
如何在gcc中启用相同的优化?
要在gcc中启用相同的优化,请使用标志-fmerge-all-constants。结果代码的大小将与clang的代码几乎相同(请参见:https://godbolt.org/z/Ldx_qe):
getDirString3(unsigned int):
xor eax, eax
cmp edi, 7
ja .L1
mov edi, edi
mov rax, QWORD PTR strings.2080[0+rdi*8]
为什么默认情况下在gcc中将其禁用?
从gcc文档website:
-fmerge-all-constants ...类似C或C ++的语言需要每个变量,包括在同一变量中的多个实例 递归调用以具有不同的位置,因此使用此选项 导致行为不合格。
在上面的示例中,优化是安全的,并且遵循as-if rule,因为未使用此本地数组的地址,也未将其与来自不同调用的地址进行比较。但是,这种优化似乎在某些情况下会导致不冲突的行为。您可以检查以下情况:here和here。
那么,您应该使用-fnmerge-all-constants标志吗?
两个编译器都不会检查天气是否使用了常量地址,因此我不建议这样做。在您的示例中显而易见,优化是安全的,但是您无法每次都知道编译器将在何处进行优化(否则您将自己完成优化),并且不确定该优化有时不会导致非冲突行为。