c++ - std::make_unique 导致大幅减速?

标签 c++ stl g++ c++14 clang++

我最近开始使用 C++14 而不是 C++11 对我的 C++ 代码库进行现代化改造。

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

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

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

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

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

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

当我查看上面生成的测试程序程序集时,我怀疑使用 std::make_unique 的 C++14 版本会在使用 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

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

问题:

初始化内存是 std::make_unique 的已知特性吗?如果不是,还有什么可以解释我遇到的性能下降?

最佳答案

Is initializing memory a known feature of std::make_unique?

这取决于您所说的“已知”是什么意思。但是,是的,这就是您的案例之间的区别。来自 cppreference , make_unique<T>(size)调用:

unique_ptr<T>(new typename std::remove_extent<T>::type[size]())
//                                                         ~~~~

就是这样 specified .

new char[size]分配内存并默认初始化它。 new char[size]()分配内存并对其进行值初始化,在 char 的情况下进行零初始化.默认情况下,标准库中的很多东西都会值初始化而不是默认初始化。

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

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


C++20 将引入新的辅助函数来缓解这个问题,由 P11020R1 提供:

  • make_unique_default_init
  • make_shared_default_init
  • allocate_shared_default_init

鉴于 make_unique<T>()会做new T() ,这些都可以new T .也就是说,没有额外的归零。在 OP 的特定情况下,std::make_unique_default_init<char[]>(size)就是你想要的。

关于c++ - std::make_unique 导致大幅减速?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49818536/

相关文章:

c++ - 从 Circle 类 C++ 返回 bool 值

c++ - 声明 unsigned int 和数组(将具有不同大小)的 unordered_map?

c++ - 使用较小的数组初始化 C++ std::array

c++ - “stoi”未在此范围内声明

c++ - 代码使用 g++ 编译但使用 VC++ 10 失败

c++ - 为什么我不能使用 "const string* sp = 0"在构造函数中初始化

c++ - 模棱两可的部分模板特化

c++ - 如何将 lambda 的 operator() 声明为 noreturn?

c++ - 在调试器中的函数评估期间中断

c++ - 什么数据结构可以在 O(log n) 时间内找到给定范围内的元素数量?