Рассмотрим этот случай:
for (...)
{
const size_t count = ...
for (size_t i = 0; i < count; ++i)
{
calculate(i); // thread-safe function
}
}
Какое наиболее элегантное решение для максимизации производительности с использованием C ++ 17 и / или Boost?
Cycli c "create + join" потоки не имеют смысла из-за огромных накладных расходов (которые в моем случае в точности равны возможному выигрышу).
Поэтому мне нужно создать N потоков только один раз и синхронизировать их с основным (используя: mutex, shared_mutex, condition_variable , atomi c, et c.). В такой обычной и понятной ситуации (чтобы все было по-настоящему безопасно и быстро) это оказалось довольно сложной задачей. Придерживаясь его в течение нескольких дней, я чувствую, что «изобретаю велосипед» ...
- Обновление 1: calculate (x) и calculate (y) могут (и должны) выполняться параллельно
- Обновление 2: std :: atomi c :: fetch_add (или что-то в этом роде) предпочтительнее очереди (или чего-то еще).
- Обновление 3: экстремальные вычисления (т.е. миллионы «внешних» вызовов и сотни «внутренних»)
- Обновление 4: calculate () изменяет данные внутреннего объекта без возврата значения
Промежуточное решение
По какой-то причине «asyn c + wait» намного быстрее, чем потоки «create + join». Таким образом, эти два примера увеличивают скорость на 100%:
Пример 1
for (...)
{
const size_t count = ...
future<void> execution[cpu_cores];
for (size_t x = 0; x < cpu_cores; ++x)
{
execution[x] = async(launch::async, ref(*this), x, count);
}
for (size_t x = 0; x < cpu_cores; ++x)
{
execution[x].wait();
}
}
void operator()(const size_t x, const size_t count)
{
for (size_t i = x; i < count; i += cpu_cores)
{
calculate(i);
}
}
Пример 2
for (...)
{
index = 0;
const size_t count = ...
future<void> execution[cpu_cores];
for (size_t x = 0; x < cpu_cores; ++x)
{
execution[x] = async(launch::async, ref(*this), count);
}
for (size_t x = 0; x < cpu_cores; ++x)
{
execution[x].wait();
}
}
atomic<size_t> index;
void operator()(const size_t count)
{
for (size_t i = index.fetch_add(1); i < count; i = index.fetch_add(1))
{
calculate(i);
}
}
Можно ли сделать это еще быстрее, создав потоки только один раз, а затем синхронизируя их с небольшими накладными расходами?
Окончательное решение
Дополнительно + 20% увеличения скорости в сравнение с std :: asyn c!
for (size_t i = 0; i < _countof(index); ++i) { index[i] = i; }
for_each_n(par_unseq, index, count, [&](const size_t i) { calculate(i); });
Можно ли избежать «индекса» избыточного массива?
Да:
for_each_n(par_unseq, counting_iterator<size_t>(0), count,
[&](const size_t i)
{
calculate(i);
});