编译器优化了内存分配
Memory allocation optimized away by compilers
Chandler Carruth在他的演讲"算法的效率,数据结构的性能"中谈到了在C++中需要更好的分配器模型。当前的分配器模型侵入了类型系统,因此几乎不可能在许多项目中工作。另一方面,Bloomberg分配器模型不侵入类型系统,而是基于虚拟函数调用,这使得编译器无法"看到"分配并对其进行优化。在他的演讲中,他谈到了编译器消除重复内存分配(1:06:47)。
我花了一些时间找到了一些内存分配优化的例子,但我发现了这个在clang下编译的代码示例,它优化了所有的内存分配,只返回1000000而没有分配任何东西。
template<typename T>
T* create() { return new T(); }
int main() {
auto result = 0;
for (auto i = 0; i < 1000000; ++i) {
result += (create<int>() != nullptr);
}
return result;
}
以下文件http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3664.html还说,分配可以在编译器中融合,这似乎表明一些编译器已经做了这类事情。
由于我对高效内存分配策略非常感兴趣,我真的很想理解为什么Chandler Carruth反对Bloomberg模型中的虚拟调用。上面的例子清楚地表明,当clang能够看到分配时,它会对事情进行优化。
- 我希望有一个"现实生活中的代码",其中这种优化是有用的,并由任何当前的编译器完成
- 你有没有任何代码的例子,其中不同的分配是由当前的编译器融合的
- 当Chandler Carruth在1:06:47的演讲中说编译器可以"消除重复"你的分配时,你明白他的意思吗
我发现了这个惊人的例子,它回答了最初问题的第一点。第2点和第3点都还没有答案。
#include <iostream>
#include <vector>
#include <chrono>
std::vector<double> f_val(std::size_t i, std::size_t n) {
auto v = std::vector<double>( n );
for (std::size_t k = 0; k < v.size(); ++k) {
v[k] = static_cast<double>(k + i);
}
return v;
}
void f_ref(std::size_t i, std::vector<double>& v) {
for (std::size_t k = 0; k < v.size(); ++k) {
v[k] = static_cast<double>(k + i);
}
}
int main (int argc, char const *argv[]) {
const auto n = std::size_t{10};
const auto nb_loops = std::size_t{300000000};
// Begin: Zone 1
{
auto v = std::vector<double>( n, 0.0 );
auto start_time = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < nb_loops; ++i) {
auto w = f_val(i, n);
for (std::size_t k = 0; k < v.size(); ++k) {
v[k] += w[k];
}
}
auto end_time = std::chrono::high_resolution_clock::now();
auto time = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
std::cout << time << std::endl;
std::cout << v[0] << " " << v[n - 1] << std::endl;
}
// End: Zone 1
{
auto v = std::vector<double>( n, 0.0 );
auto w = std::vector<double>( n );
auto start_time = std::chrono::high_resolution_clock::now();
for (std::size_t i = 0; i < nb_loops; ++i) {
f_ref(i, w);
for (std::size_t k = 0; k < v.size(); ++k) {
v[k] += w[k];
}
}
auto end_time = std::chrono::high_resolution_clock::now();
auto time = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
std::cout << time << std::endl;
std::cout << v[0] << " " << v[n - 1] << std::endl;
}
return 0;
}
其中在具有f_val的for循环中没有发生单个存储器分配。不过,这种情况只发生在Clang身上(Gcc和icpc在这方面都失败了),当构建一个稍微复杂一点的示例时,不会进行优化。
相关文章:
- 在c++中为我自己的基于指针的数组分配内存的正确方法
- 给定一个指向堆分配内存的指针,智能指针实现如何为其找到合适的释放函数?
- 如果 const 不分配内存,为什么我可以获取 const 的地址?
- 在函数中分配内存时出现问题
- 如何为 std::vector 分配内存,然后稍后为某些元素调用构造函数?
- constexpr new 如何分配内存?
- 在构造函数中分配内存失败是如何冒泡的
- LLVM 传递以在特定地址分配内存
- CudaMalloc 在分配内存时失败
- 为什么它在不分配内存的情况下工作正常
- 为什么在正确解除分配内存时出现内存泄漏?
- 如何通过 malloc 为队列数组分配内存?
- vector是否为std::移动的对象连续分配内存
- 删除类成员的动态分配内存的最佳方法是什么
- 唯一指针是否在堆或堆栈上分配内存?
- 如果不分配内存,我如何能够为变量创建和分配值?
- std::initializer_list 堆是否分配内存?
- 如何按顺序或在指定的地址分配内存?
- 是否可以使用 malloc 为类对象分配内存?
- 迭代器是否分配内存(如指针)?