c++ - 令人惊讶的是, vector <char> 的自定义分配器效率低下

标签 c++ c++11 allocator

我想使用带有下面自定义分配器的 vector ,其中 construct()destroy() 有一个空的主体:

struct MyAllocator : public std::allocator<char> {
    typedef allocator<char> Alloc;
    //void destroy(Alloc::pointer p) {} // pre-c+11
    //void construct(Alloc::pointer p, Alloc::const_reference val) {} // pre-c++11
    template< class U > void destroy(U* p) {}
    template< class U, class... Args > void construct(U* p, Args&&... args) {}
    template<typename U> struct rebind {typedef MyAllocator other;};
};

现在,由于我在 another question 中指定的原因, vector 必须在循环中多次调整大小。为了简化性能测试,我做了一个非常简单的循环,如下所示:

std::vector<char, MyAllocator> v;
v.reserve(1000000); // or more. Make sure there is always enough allocated memory
while (true) {
   v.resize(1000000);
   // sleep for 10 ms
   v.clear(); // or v.resize(0);
};

我注意到,尽管分配器具有空的 construct()destroy() 成员函数,但更改大小会使 CPU 消耗从 30% 增加到 80% 。因此,我预计对性能的影响非常小或根本没有影响(启用优化)。消费增量如何可能?第二个问题是:为什么在调整大小后读取内存时,我看到调整大小的内存中每个字符的值为 0 (我期望一些非零值,因为 constuct() 确实没有什么) ?

我的环境是 g++4.7.0 ,启用了-O3级别优化。 PC Intel 双核,4GB 可用内存。显然对 construct 的调用根本无法优化?

最佳答案

已更新

这是一个完整的重写。原始帖子/我的答案中有一个错误,这使我对同一分配器进行了两次基准测试。哎呀。

嗯,我可以看到性能上的巨大差异。我制作了以下测试台,它采取了一些预防措施来确保关键的东西不会完全优化。然后,我验证(使用 -O0 -fno-inline)分配器的 constructdestruct 调用是否被调用了预期的次数(是):

#include <vector>
#include <cstdlib>

template<typename T>
struct MyAllocator : public std::allocator<T> {
    typedef std::allocator<T> Alloc;
    //void destroy(Alloc::pointer p) {} // pre-c+11
    //void construct(Alloc::pointer p, Alloc::const_reference val) {} // pre-c++11
    template< class U > void destroy(U* p) {}
    template< class U, class... Args > void construct(U* p, Args&&... args) {}
    template<typename U> struct rebind {typedef MyAllocator other;};
};

int main()
{
    typedef char T;
#ifdef OWN_ALLOCATOR
    std::vector<T, MyAllocator<T> > v;
#else
    std::vector<T> v;
#endif
    volatile unsigned long long x = 0;
    v.reserve(1000000); // or more. Make sure there is always enough allocated memory
    for(auto i=0ul; i< 1<<18; i++) {
        v.resize(1000000);
        x += v[rand()%v.size()];//._x;
        v.clear(); // or v.resize(0);
    };
}

时间差异已标记:

g++ -g -O3 -std=c++0x -I ~/custom/boost/ test.cpp -o test 

real    0m9.300s
user    0m9.289s
sys 0m0.000s

g++ -g -O3 -std=c++0x -DOWN_ALLOCATOR -I ~/custom/boost/ test.cpp -o test 

real    0m0.004s
user    0m0.000s
sys 0m0.000s

我只能假设您所看到的内容与标准库优化 char 的分配器操作(它是 POD 类型)有关。

当您使用时,时间间隔会更远

struct NonTrivial
{
    NonTrivial() { _x = 42; }
    virtual ~NonTrivial() {}
    char _x;
};

typedef NonTrivial T;

在这种情况下,默认分配器需要超过 2 分钟(仍在运行)。 而“虚拟”MyAllocator 花费约 0.006 秒。 (注意,这会调用未定义的行为,引用尚未正确初始化的元素。)

关于c++ - 令人惊讶的是, vector <char> 的自定义分配器效率低下,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15237449/

相关文章:

c++ - 是否有自动检测我应该编译哪些 .cpp 源文件的方法(取决于#include *.h)

c++ - 你有没有写过没有守卫的标题?

c++ - 如何在不导致复制的情况下将 char 数据附加到 std::vector

c++ - 分配器 : how are the standard containers expected to work internally?

C++ 内存分配器和多态类型

c++ - 自建llvm opt的 undefined symbol ?

c++ - 在 C++ 类定义中包含 Objective C++ 类型

c++ - 使用可变参数模板的随机选择器函数——这可能吗?

c++ - 将基于 C++11 范围的 for 循环与右值 range-init 一起使用是否安全?

c++ - 编译器支持 STL 容器中的有状态分配器