std :: make_unique导致大幅放缓?

Lin*_*umz 19 c++ stl g++ clang++ c++14

我最近开始使用C++ 14而不是C++ 11来使我的C++代码库现代化.

在用C++ 14 替换了一次std::unique_ptr.reset(new ...)使用后std::make_unique,我意识到我的测试套件(由大约30个C++测试程序组成)运行速度慢了约50%.

旧C++ 11代码(快速):

class Foo
{
  public:
    Foo(size_t size)
    {
        array.reset(new char[size]);
    }
  private:
    std::unique_ptr<char[]> array;
};
Run Code Online (Sandbox Code Playgroud)

新的C++ 14代码(慢):

class Foo
{
  public:
    Foo(size_t size)
    {
        array = std::make_unique<char[]>(size);
    }
  private:
    std::unique_ptr<char[]> array;
};
Run Code Online (Sandbox Code Playgroud)

使用C++ 14代码时,GCC和Clang都运行得慢得多std::make_unique.当我使用valgrind测试两个版本时,它报告C++ 11和C++ 14代码使用相同数量的分配和相同数量的已分配内存,并且没有内存泄漏.

当我查看上面生成的测试程序的程序集时,我怀疑C++ 14版本std::make_unique在使用memset分配后使用重置内存.C++ 11版本没有这样做:

C++ 11程序集(GCC 7.4,x64)

main:
sub rsp, 8
movsx rdi, edi
call operator new[](unsigned long)
mov rdi, rax
call operator delete[](void*)
xor eax, eax
add rsp, 8
ret
Run Code Online (Sandbox Code Playgroud)

C++ 14程序集(GCC 7.4,x64)

main:
push rbx
movsx rbx, edi
mov rdi, rbx
call operator new[](unsigned long)
mov rcx, rax
mov rax, rbx
sub rax, 1
js .L2
lea rax, [rbx-2]
mov edx, 1
mov rdi, rcx
cmp rax, -1
cmovge rdx, rbx
xor esi, esi
call memset
mov rcx, rax
.L2:
mov rdi, rcx
call operator delete[](void*)
xor eax, eax
pop rbx
ret
Run Code Online (Sandbox Code Playgroud)

问题:

初始化内存是一个已知的功能std::make_unique?如果没有其他什么可以解释我遇到的性能下降?

Bar*_*rry 21

初始化内存是一个已知的功能std::make_unique

这取决于你所说的"已知".但是,这是你的案件之间的区别.从cppreference,make_unique<T>(size)调用确实:

unique_ptr<T>(new typename std::remove_extent<T>::type[size]())
//                                                         ~~~~
Run Code Online (Sandbox Code Playgroud)

这是如何指定的.

new char[size]分配内存并默认初始化它.new char[size]()分配内存和值初始化它,在这种情况下零初始化char.默认情况下,标准库中的许多内容将进行值初始化而不是默认初始化.

同样,make_unique<T>()new T(),不new T...... make_unique<char>()给你0,new char给你一个不确定的值.

作为一个类似的例子,如果我想要一个给定大小vector<char>未初始化缓冲区的调整大小(立即由其他东西填充),我必须使用我自己的分配器或使用不属于char它的类型初始化.

  • @rubenvb:我认为谎言只是像`my_char()`这样的构造函数通常会让你相信它会处理所有数据成员的正确初始化. (4认同)
  • @rubenvb•它没有搞砸,这是故意的谎言,以免在不需要时支付初始化性能. (4认同)
  • @rubenvb例如`struct my_char {my_char(){/*nothing*/} char c; };` (2认同)