C++ 构造大向量shared_ptr到类的有效方法
C++ Efficient way to construct large vector of shared_ptr to class
我需要构造一个大std::vector<std::shared_ptr<A>> many_ptr_to_A
。
理想情况下,对于A
,使用带有参数的非默认构造函数。下面的代码示例中定义了几个变体:
#include <iostream>
#include <vector>
#include <memory>
#include <ctime>
class A
{
public:
A(std::vector<double> data):
data(data)
{}
A():
data(std::vector<double>(3, 1.))
{}
std::vector<double> data;
};
int main()
{
int n = 20000000;
std::vector<std::shared_ptr<A>> many_ptr_to_A;
// option 1
std::clock_t start = std::clock();
std::vector<A> many_A(n, std::vector<double>(3, 1.));
std::cout << double(std::clock() - start) / CLOCKS_PER_SEC << std::endl;
// end option 1
many_ptr_to_A.clear();
// option 2
start = std::clock();
many_ptr_to_A.reserve(n);
for (int i=0; i<n; i++) {
many_ptr_to_A.push_back(std::shared_ptr<A>(new A(std::vector<double>(3, 1.))));
}
std::cout << double(std::clock() - start) / CLOCKS_PER_SEC << std::endl;
// end option 2
many_ptr_to_A.clear();
// option 3
start = std::clock();
A* raw_ptr_to_A = new A[n];
for (int i=0; i<n; i++) {
many_ptr_to_A.push_back(std::shared_ptr<A>(&raw_ptr_to_A[i]));
}
std::cout << double(std::clock() - start) / CLOCKS_PER_SEC << std::endl;
// end option 3
return 0;
}
选项 1
相当快,但不幸的是我需要指针而不是原始对象。创建指向结果分配空间的指针并防止向量删除对象的方法会很棒,但我想不出一个。
选项 2
这有效,我可以在构造函数中为每个A
提供特定数据。不幸的是,这是相当缓慢的。使用std::make_shared
而不是new
并不能真正改善这种情况。
更糟糕的是,在多个线程中使用时,这似乎是一个很大的瓶颈。假设我使用 2 在 2 个线程中运行选项 10 n_thread = n / 10
,而不是快十倍左右,整个事情慢了大约四倍。为什么会这样?当多线程尝试分配许多小块内存时,是否有问题?
我使用的服务器上的内核数大于线程数。我的应用程序的其余部分可以很好地扩展内核数量,因此这实际上代表了一个瓶颈。
不幸的是,我在并行化方面并没有真正的经验......
选项 3
通过这种方法,我尝试将快速分配与原始new
一次性和shared_ptrs相结合。这会编译,但不幸的是,当调用向量的析构函数时会产生分割错误。我不完全明白为什么会发生这种情况。是因为A
不是 POD?
我会在对象创建后手动将特定于对象的数据填充到对象中。
问题
如何以有效的方式执行大量A
shared_ptr
的分配,以便在许多线程/内核上使用时也能很好地扩展?我是否错过了一种一次性构建std::vector<std::shared_ptr<A>> many_ptr_to_A
的明显方法?
我的系统是 Linux/Debian 服务器。我使用 g++ 和 -O3 编译,-std=c++11 选项。
任何帮助都非常感谢:)
选项 3 是未定义的行为,您有n
shared_ptrs,它们都将尝试delete
单个A
,但整个数组必须只有一个delete[]
,不能delete
n
次使用。不过,您可以这样做:
std::unique_ptr<A[]> array{ new A[n] };
std::vector<std::shared_ptr<A>> v;
v.reserve(n);
v.emplace_back(std::move(array));
for (int i = 1; i < n; ++i)
v.push_back(std::shared_ptr<A>{v[0], v[0].get() + i});
这将创建一个数组,然后创建n
shared_ptr对象,这些对象都共享数组的所有权,并且每个对象都指向数组的不同元素。这是通过创建一个拥有数组(和一个合适的删除器)的shared_ptr
,然后创建别名第一个数组的n-1
shared_ptrs来完成的,即共享相同的引用计数,即使它们的get()
成员将返回不同的指针。
首先用数组初始化一个unique_ptr<A[]>
,以便default_delete<A[]>
将用作删除器,并且该删除器将被转移到第一个shared_ptr
,以便当最后一个shared_ptr
放弃所有权时,将使用权利delete[]
来释放整个数组。要获得相同的效果,您可以像这样创建第一个shared_ptr:
v.push_back(std::shared_ptr<A>{new A[n], std::default_delete<A[]>{}});
或:
v.emplace_back(std::unique_ptr<A[]>{new A[n]});
- 在C++中初始化向量映射的最有效方法
- 将此布尔值传递给此函数的最有效方法是什么?
- 比较C++变量的最有效方法
- 在 c++ 中解决段树以外的范围查询的有效方法是什么?
- 存储变量的更有效方法是什么?
- 确保套装新鲜度的有效方法
- 当映射包含字符串向量作为值时,从值中获取键的有效方法
- 映射唯一值和重复值的有效方法.可以访问键或值的位置
- 在C++事务之间存储大量字符数据的有效方法
- 在unordered_multimap中精确迭代一次每个键的有效方法
- 一种将 Dart 中的字节数据转换为 C++ 中的无符号字符*的有效方法?
- 检查两个向量是否并行的最有效方法
- 从浮点数中删除小数部分但保留类型的有效方法
- 传递非泛型函数的最有效方法是什么?
- 按升序打印矢量的所有元素直到它为空而没有重复项的最有效方法是什么?
- 创建字符串数组的有效方法
- 返回一个引用C++中另一个类对象的对象的有效方法
- C++去除前x个元素的有效方法,在不改变向量大小的情况下将第x+1个元素推到第一个
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 从std::map值中获取密钥的有效方法