c++ - 使用 std::async 并将向量中的参数传递给函数并收集结果
问题描述
我想让这个函数并行计算:
#include <iostream>
#include <vector>
int compute_something(int i, int j) {
return i*j;
}
int main() {
auto params = std::vector<int>(1000,5);
std::vector<int> results;
for (auto i : params)
results.push_back(compute_something(i,4));
return 0;
}
我将参数从列表传递给函数,并希望按顺序返回结果。我想使用异步并行化,因为compute_something()
根据输入需要不同的时间。输入向量也远大于核心数。
解决方案
在 C++17 中,标准算法以并行版本提供。您指定执行策略( std::seq
)、并行 ( std::par
) 或并行和矢量化 ( std::par_unseq
),它将在后台为您执行多线程。
因此,对于您想要做的事情,您可以使用std::transform
lambda 函数来捕获您想要对输入向量的每个元素执行的操作,并将结果放入results
向量中(大小必须相同):
#include <execution>
#include <algorithm>
#include <vector>
int compute_something(int i, int j) {
return i * j;
}
int main()
{
auto params = std::vector<int>(1000, 5);
std::vector<int> results(1000, 0);
std::transform(std::execution::par_unseq, params.begin(), params.end(),
results.begin(), [](int i) { return compute_something(i, 4); }
);
}
当然,可以将计算嵌入到 lambda 中,以进行像您在compute_something
. 那么代码就变成了:
std::transform(std::execution::par_unseq, params.begin(), params.end(),
results.begin(), [](int i) { return i * 4; }
并不是所有的编译器都已经实现了执行策略。因此,如果您的编译器不支持它,您可以采用另一种方式:std::async
以块的形式使用和处理输入向量。为此,您必须定义一个接受迭代器并返回结果向量的新函数。然后你可以在最后组合结果。
例子:
#include <future>
#include <vector>
using Iter = std::vector<int>::iterator;
std::vector<int> parallel_compute(Iter beg, Iter end)
{
std::vector<int> results;
//Reserve memory to avoid reallocations
auto size = std::distance(beg, end);
results.reserve(size);
for (Iter it = beg; it != end; ++it)
{
results.push_back(*it * 4); //Add result to vector
}
return results;
}
int main()
{
const int Size = 1000;
//Chunk size
const int Half = Size / 2;
//Input vector
auto params = std::vector<int>(Size, 5);
//Create futures
auto fut1 = std::async(std::launch::async, parallel_compute, params.begin(), params.begin()+ Half);
auto fut2 = std::async(std::launch::async, parallel_compute, params.begin()+ Half, params.end());
//Get results
auto res1 = fut1.get();
auto res2 = fut2.get();
//Combine results into one vector
std::vector<int> results;
results.insert(results.end(), res1.begin(), res1.end());
results.insert(results.end(), res2.begin(), res2.end());
}
该launch::async
策略将确保创建两个线程。但是,我不会创建太多线程 - 每个内核一个是合理的策略。您可以利用std::thread::hardware_concurrency()
获取系统支持的并发线程数。创建线程并对其进行管理会带来一些开销,如果创建太多线程可能会适得其反。
编辑:
为了避免对单个小向量进行昂贵的分配,我们可以在开始时创建一个结果向量,并将迭代器传递给每个并行调用的结果范围parallel_compute
。由于每个线程将访问结果向量的不同部分,因此我们不需要同步:
#include <future>
#include <vector>
using Iter = std::vector<int>::iterator;
void parallel_compute(Iter beg, Iter end, Iter outBeg)
{
for (Iter it = beg; it != end; ++it)
{
*outBeg++ = (*it * 4); //Add result to vector
}
}
int main()
{
const int Size = 1000;
//Chunk size
const int Half = Size / 2;
//Input vector
auto params = std::vector<int>(Size, 5);
//Output vector
std::vector<int> results(Size, 0);
//Create futures
auto fut1 = std::async(std::launch::async, parallel_compute, params.begin(), params.begin() + Half, results.begin());
auto fut2 = std::async(std::launch::async, parallel_compute, params.begin() + Half, params.end(), results.begin() + Half);
//Get results
fut1.wait();
fut2.wait();
}
推荐阅读
- c# - 如何使用用户输入更新存储在 c# 中的 sqlite 表中的数据
- javascript - 为什么 Handlebars {{body}} 没有将我的标签作为 html 接收?
- python - 如何在 Python 中用空格替换文本中的特殊字符?
- ios - Facebook 登录 ViewController 不存在
- python - 从元组列表创建唯一集
- angular - 带参数的 Angular 路由 RootPath
- haskell - Haskell 按索引更新子列表
- java - 在 switch 语句中使用 char 数据
- assembly - 为什么我们在汇编中添加一个值来初始化堆栈指针(R7)?
- amazon-web-services - AWS EC2 实例连接由端口 22 重置