Мне сказали несколько раз, что я должен использовать std::async
для огня & Забудьте о типе задач с параметром std::launch::async
(так что это делает магию на новом потоке исполнения).Почему std :: async медленнее по сравнению с простыми отдельными потоками?
Воодушевленные этими заявлениями, я хотел бы видеть, как std::async
сравнивается с:
- последовательного выполнения
- простой отдельностоящий
std::thread
- мой простой асинхронной "реализация"
Мои наивная реализация async выглядит так:
template <typename F, typename... Args>
auto myAsync(F&& f, Args&&... args) -> std::future<decltype(f(args...))>
{
std::packaged_task<decltype(f(args...))()> task(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
auto future = task.get_future();
std::thread thread(std::move(task));
thread.detach();
return future;
}
Ничего особенного здесь не упаковывает функтор f
в std::packaged task
вместе со своими аргументами, запускает его на новый std::thread
, который отделяется и возвращается с std::future
от задачи.
И теперь код измерения времени выполнения с std::chrono::high_resolution_clock
:
int main(void)
{
constexpr unsigned short TIMES = 1000;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < TIMES; ++i)
{
someTask();
}
auto dur = std::chrono::high_resolution_clock::now() - start;
auto tstart = std::chrono::high_resolution_clock::now();
for (int i = 0; i < TIMES; ++i)
{
std::thread t(someTask);
t.detach();
}
auto tdur = std::chrono::high_resolution_clock::now() - tstart;
std::future<void> f;
auto astart = std::chrono::high_resolution_clock::now();
for (int i = 0; i < TIMES; ++i)
{
f = std::async(std::launch::async, someTask);
}
auto adur = std::chrono::high_resolution_clock::now() - astart;
auto mastart = std::chrono::high_resolution_clock::now();
for (int i = 0; i < TIMES; ++i)
{
f = myAsync(someTask);
}
auto madur = std::chrono::high_resolution_clock::now() - mastart;
std::cout << "Simple: " << std::chrono::duration_cast<std::chrono::microseconds>(dur).count() <<
std::endl << "Threaded: " << std::chrono::duration_cast<std::chrono::microseconds>(tdur).count() <<
std::endl << "std::sync: " << std::chrono::duration_cast<std::chrono::microseconds>(adur).count() <<
std::endl << "My async: " << std::chrono::duration_cast<std::chrono::microseconds>(madur).count() << std::endl;
return EXIT_SUCCESS;
}
Где someTask()
это простой метод, где я жду немного, имитируя некоторые работы:
void someTask()
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
Наконец, мои результаты :
- Последовательный: 1263615
- Каскадный: 47111
- станд :: синхронизация: 821441
- Мой асинхронной: 30784
Может кто объяснить эти результаты? Кажется, что std::aysnc
много медленнее, чем моя наивная реализация, или просто и просто снятstd::thread
s. Почему это? После этих результатов есть какая-то причина для использования std::async
?
(Обратите внимание, что я сделал этот тест с лязгом ++ и г ++ тоже, и результаты были очень похожи)
UPDATE:
После прочтения ответа Дейва S, я обновил свой небольшой тест следующим образом:
std::future<void> f[TIMES];
auto astart = std::chrono::high_resolution_clock::now();
for (int i = 0; i < TIMES; ++i)
{
f[i] = std::async(std::launch::async, someTask);
}
auto adur = std::chrono::high_resolution_clock::now() - astart;
Таким образом, std::future
s теперь не уничтожены - и, таким образом, соединены - каждый пробег. После этого изменения кода std::async
дает похожие результаты для моей реализации & снят std::thread
s.
Я уверен, что это не проблема, но мне просто нужно задать полноту информации. Вы измеряете отладочную (неоптимизированную) или выпущенную (оптимизированную) сборку? Я предполагаю оптимизированную сборку, так как в противном случае любые измерения будут бессмысленными, но я должен спросить. –
@JesperJuhl Полностью действительный вопрос, но я измеряю -O2. –