【问题标题】:Is initializing a vector using `std::transform` + parallel execution policy more memory intensive compared to more traditional methods?与更传统的方法相比,使用 `std::transform` + 并行执行策略初始化向量是否更占用内存?
【发布时间】:2021-02-16 02:46:53
【问题描述】:

我有一个非常大的向量vector<T> t_vec

我有一个类U,它实现了一个带有签名U(T x)的构造函数。

我可以使用两种方法创建vector<U> u_vec

一)

vector<U> u_vec(t_vec.size());
std::transform(std::execution::parallel_policy, 
    t_vec.begin(), 
    t_vec.end(), 
    u_vec.begin(), 
    [](T& t) { return U(t); }
);

B)

vector<U> u_vec(t_vec.begin(), t_vec.end());

我应该期望方法 A) 比方法 B) 更占用内存吗?

【问题讨论】:

  • U 是否也有默认构造函数和赋值运算符?方法 (A) 依赖于这些。
  • 我不明白为什么 (A) 应该使用比 (B) 更多的内存。我想它可能会使用O(1) 额外内存,因为它可以同时处理多个临时U 实例。
  • 启动 threafs 确实需要额外的内存,如果只是为了堆栈空间。每个线程至少 4K,加上内核分配的其他线程数据。
  • @MichaëlRoy -- 这可能很重要,但通常,并行算法使用线程池,其中已经创建了线程。为并行算法动态创建线程会增加时间开销。
  • 皮特贝克尔。我同意。但我们必须记住,线程池是按需创建的,最终在不使用时会超时。 @user89 如果这可能是一个问题,不时调用 async() 可以保持线程正常运行。

标签: c++ vector parallel-processing initialization c++-standard-library


【解决方案1】:

在现代 C++ 中,如果

auto f = [](T& t) { return U(t); };

我们这样做:

std::aligned_storage_t<sizeof(U), alignof(U)> buffer;
T t;
::new( (void*)&buffer ) U( f(t) );

f 的返回值被直接省略到缓冲区中。

作为evidence,您可以看到编译器这样做而无需调用移动构造函数。

这在 中显示为“保证省略”。

现在,您的代码中可能存在一些内存开销; parallel_policy 意味着使用多个线程,管理这些线程需要非零内存。但与非并行版本相比,它不会为所述Us 分配多个std::vectors 或更多U 实例或更多存储空间。

我相信(只有 80% 的信心)用于parallel_policy 的线程必须在逻辑上重新创建(例如,具有不同的thread_local),但操作系统级线程可以重复用于多个此类调用(a水池)。跟踪说thread_local 状态,维护这样一个池等将使用每个线程的固定内存量,如果您的代码从未使用过使用parallel_policy 的线程,那么您的程序的总内存占用量将增加O(核心数)。

【讨论】:

  • 您的第一个示例不适用,因为 std::transform 调用 U::operator= 而不是任何构造函数(实现临时也是如此)
猜你喜欢
  • 2020-02-04
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 2018-05-03
  • 2022-07-06
  • 2016-08-17
相关资源
最近更新 更多