内存分配由编译器优化

Ins*_*oop 8 c++ memory optimization memory-management allocation

在他的演讲"效率与算法,性能与数据结构"中,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;
}
Run Code Online (Sandbox Code Playgroud)

以下论文http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3664.html也说分配可以在编译器中融合,似乎表明一些编译器已经做了那种排序东西的.

由于我对高效内存分配的策略非常感兴趣,我真的很想理解为什么Chandler Carruth反对Bloomberg模型中的虚拟调用.上面的例子清楚地表明,clang可以在看到分配时优化.

  1. 我希望有一个"真实代码",这个优化是有用的,并且由任何当前的编译器完成
  2. 您是否有任何代码示例,其中不同的分配由anu当前编译器融合?
  3. 你是否理解Chandler Carruth在1:06:47他的讲话中说编译器可以"重复删除"你的分配时的含义?

Ins*_*oop 2

我发现这个令人惊奇的例子回答了最初问题的第一点。第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;
}
Run Code Online (Sandbox Code Playgroud)

其中,在使用 f_val 的 for 循环中没有发生任何内存分配。不过,这只发生在 Clang 上(Gcc 和 icpc 都失败了),并且在构建稍微复杂一点的示例时,优化并未完成。