【发布时间】:2015-08-11 19:45:34
【问题描述】:
我在看一个谈话,"Efficiency with Algorithms, Performance with Data Structures",然后 对以下评论感到惊讶:
#include <string>
#include <unordered_map>
#include <memory>
struct Foo {
int x;
};
Foo* getFoo(std::string key,
std::unordered_map<std::string,
std::unique_ptr<Foo>> &cache) {
if (cache[key])
return cache[key].get();
cache[key] = std::unique_ptr<Foo>(new Foo());
return cache[key].get();
}
Foo* getFooBetter(std::string key,
std::unordered_map<std::string,
std::unique_ptr<Foo>> &cache) {
std::unique_ptr<Foo> &entry = cache[key];
if (entry)
return entry.get();
entry = std::unique_ptr<Foo>(new Foo());
return entry.get();
}
getFooBetter() 更好。我一直相信我可以依靠
在编译器上以相同的方式执行这种转换
我希望只评估多次出现的x+y
一次。不出所料,生成的 LLVM IR 确实与
主持人。即使使用 -O9,我们仍然需要对 cache[key] 进行 3 次调用
getFoo() 版本。
我已将较长的 LLVM IR of both with c++ symbols unmangled 移到不符合要求的位置,以免造成视觉上的冒犯。
Another StackOverflow question 揭示了这里的部分答案是operator[]
假定能够修改它希望的任何全局状态,并且
因此我们不能忽略调用。 A linked proposal关于介绍一个
[[pure]] 注解讲述了它在 CSE 中的应用。
如果我们住 4 个电话,我就可以在这里感到满意了。
但是,如果我对 IR 的解读是正确的,看起来我们优化了
getFoo() 好像我们写的:
Foo* getFoo(std::string key,
std::unordered_map<std::string,
std::unique_ptr<Foo>> &cache) {
if (cache[key])
return cache[key].get();
std::unique_ptr<Foo> &entry = cache[key];
entry = std::unique_ptr<Foo>(new Foo());
return entry.get();
}
有人能解释一下clang对代码的看法吗
它能够合并最后两个cache[key]s,但不是全部
他们? (我本地的 clang 是 3.4。)
【问题讨论】:
-
疯狂猜测(我不知道 Clang 内部原理):这可能是内联扩展后进一步优化的结果;在这种情况下,在扩展之后,编译器确实有足够的关于扩展片段的确切性质的信息,并且可以消除重复而不会有意外副作用的风险并且没有 [[pure]]。尽管如此,依赖编译器完成的任何类型的优化通常都是一个坏主意。
标签: c++ clang compiler-optimization