分配但不构造大量 C++ 对象
allocate but don't construct a large array of C++ objects
在某些情况下,我的 C++14 程序需要 "block" 大约 1 亿 complex<float>
,这需要接近 1 GB 的 RAM。我们可以安全地假设所需的内存可用。
但是分配一个新的 std::vector
真的很慢,因为复杂的构造函数被调用了 1 亿次。在我的机器上,代码需要大约一整秒来初始化数组。
相比之下,调用 calloc()
将分配的内存初始化为零,效果基本相同,将在非常短的毫秒数内 运行。
事实证明我们甚至不需要这个初始化,因为大型数组中的复合体将在不久后从外部源填充。因此,我正在考虑将 "block" 中对象的构造推迟到以后,然后直接从外部数据源构造它们。
所以我的问题是,是否有一种安全的惯用且高效的 C++ 方法来做到这一点,也许在整个过程中使用 C++ 移动语义?如果不是,我们决定简单地 malloc
内存块,那么我们可以简单地 reinterpret_cast
内存块到 complex<float>
的普通旧 C 数组吗?
感谢您的帮助
让-丹尼斯·穆伊斯
所以你的问题是你是否可以使用 malloc()
。多年前,我对我的旧 C++ 编译器使用了相同的方法,并且它起作用了。但最后我不得不调用 free()
而不是 delete[]
。我认为这是特定于实现的,因此您应该在编译器上尝试一下。
我强烈建议坚持使用 C++,避免自己手动管理内存。
标准库应该够用了。例如
std::vector< complex > my_vector;
// Reserve the necessary space without constructing anything
my_vector.reserve( 100'000'000);
// construct the elements when needed
populate( my_vector );
如果您将 complex<float>
class 的默认构造函数定义为空,这使得成员变量未初始化,那么考虑到编译器优化,这两个操作之间应该没有任何真正的区别已开启。
假设 complex
class.
的定义如下
template <typename T>
struct complex
{
complex() {}; // Empty constructor does nothing
T a, b;
};
在 x86-64 gcc 6.2 和启用 -O2 的情况下使用 vector
初始化生成的程序集是:
std::vector<complex<float>> v(100);
mov edi, 800
call operator new(unsigned long)
mov rdi, rax
call operator delete(void*)
而手动调用malloc
和free
生成的程序集是:
auto v = malloc(100 * sizeof(complex<float>));
free(v);
mov edi, 800
call malloc
mov QWORD PTR [rsp+8], rax
mov rdi, QWORD PTR [rsp+8]
call free
如您所见,vector
实现不再为每个元素调用 complex<float>
的构造函数。 vector
的用法更加正确和可读,并且还利用了有助于防止内存泄漏的 RAII。
在某些情况下,我的 C++14 程序需要 "block" 大约 1 亿 complex<float>
,这需要接近 1 GB 的 RAM。我们可以安全地假设所需的内存可用。
但是分配一个新的 std::vector
真的很慢,因为复杂的构造函数被调用了 1 亿次。在我的机器上,代码需要大约一整秒来初始化数组。
相比之下,调用 calloc()
将分配的内存初始化为零,效果基本相同,将在非常短的毫秒数内 运行。
事实证明我们甚至不需要这个初始化,因为大型数组中的复合体将在不久后从外部源填充。因此,我正在考虑将 "block" 中对象的构造推迟到以后,然后直接从外部数据源构造它们。
所以我的问题是,是否有一种安全的惯用且高效的 C++ 方法来做到这一点,也许在整个过程中使用 C++ 移动语义?如果不是,我们决定简单地 malloc
内存块,那么我们可以简单地 reinterpret_cast
内存块到 complex<float>
的普通旧 C 数组吗?
感谢您的帮助
让-丹尼斯·穆伊斯
所以你的问题是你是否可以使用 malloc()
。多年前,我对我的旧 C++ 编译器使用了相同的方法,并且它起作用了。但最后我不得不调用 free()
而不是 delete[]
。我认为这是特定于实现的,因此您应该在编译器上尝试一下。
我强烈建议坚持使用 C++,避免自己手动管理内存。
标准库应该够用了。例如
std::vector< complex > my_vector;
// Reserve the necessary space without constructing anything
my_vector.reserve( 100'000'000);
// construct the elements when needed
populate( my_vector );
如果您将 complex<float>
class 的默认构造函数定义为空,这使得成员变量未初始化,那么考虑到编译器优化,这两个操作之间应该没有任何真正的区别已开启。
假设 complex
class.
template <typename T>
struct complex
{
complex() {}; // Empty constructor does nothing
T a, b;
};
在 x86-64 gcc 6.2 和启用 -O2 的情况下使用 vector
初始化生成的程序集是:
std::vector<complex<float>> v(100);
mov edi, 800
call operator new(unsigned long)
mov rdi, rax
call operator delete(void*)
而手动调用malloc
和free
生成的程序集是:
auto v = malloc(100 * sizeof(complex<float>));
free(v);
mov edi, 800
call malloc
mov QWORD PTR [rsp+8], rax
mov rdi, QWORD PTR [rsp+8]
call free
如您所见,vector
实现不再为每个元素调用 complex<float>
的构造函数。 vector
的用法更加正确和可读,并且还利用了有助于防止内存泄漏的 RAII。