В C ++ 17 стандартные алгоритмы доступны в параллельной версии. Вы задаете политику выполнения (std::seq
), параллельную (std::par
) или параллельную и векторизованную (std::par_unseq
), и она будет выполнять многопоточность для вас в фоновом режиме.
Таким образом, для того, что вы хотите сделать, вы можете использовать std::transform
с лямбда-функцией для захвата операции, которую вы хотите выполнить над каждым элементом вашего входного вектора, и результаты помещаются в results
vector (размер должен быть таким же):
#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); }
);
}
Конечно, возможно встраивать вычисления в лямбду для такого простого вычисления, как в 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)
{
auto size = std::distance(beg, end);
std::vector<int> results;
results.reserve(size);
for (Iter it = beg; it != end; ++it)
{
results.push_back(*it * 4);
}
return results;
}
int main()
{
const int Size = 1000;
const int Half = Size / 2;
auto params = std::vector<int>(Size, 5);
auto fut1 = std::async(launch::async, parallel_compute, params.begin(), params.begin()+ Half);
auto fut2 = std::async(launch::async, parallel_compute, params.begin()+ Half, params.end());
auto res1 = fut1.get();
auto res2 = fut2.get();
std::vector<int> results;
results.insert(results.end(), res1.begin(), res1.end());
results.insert(results.end(), res2.begin(), res2.end());
}
Политика launch::async
обеспечит создание двух потоков. Однако я бы не стал создавать слишком много потоков - по одному на ядро - разумная стратегия. Создание потоков и управление ими вносит некоторые накладные расходы и может привести к обратным результатам, если вы создадите слишком много.