2016-05-21 4 views
18

Мне сказали несколько раз, что я должен использовать 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.

+4

Я уверен, что это не проблема, но мне просто нужно задать полноту информации. Вы измеряете отладочную (неоптимизированную) или выпущенную (оптимизированную) сборку? Я предполагаю оптимизированную сборку, так как в противном случае любые измерения будут бессмысленными, но я должен спросить. –

+0

@JesperJuhl Полностью действительный вопрос, но я измеряю -O2. –

ответ

17

Одним из ключевых отличий является то, что будущее, возвращаемое асинхронным соединением, присоединяется к потоку, когда будущее будет уничтожено или в вашем случае заменено новым значением.

Это означает, что он должен выполнить someTask() и присоединиться к нити, которая требует времени. Ни один из ваших других тестов не делает этого, где они просто порождают их самостоятельно.

+5

"* будущее, возвращаемое async, присоединяется к потоку, когда будущее уничтожается *« AKA: главная причина никогда, никогда не использовать 'std :: async'. –

+4

@NicolBolas: Если вы на самом деле не хотите, чтобы операция была завершена локально ... –

+2

@KerrekSB: Если вы этого хотели, вы должны явно называть 'future :: wait', так же, как вы делали бы для каждого другого« будущего ». Проблема в том, что 'future', возвращаемое' async', имеет другое поведение от * любого другого 'будущего' объекта *. Это непоследовательность, это проблема, а не поведение. Непоследовательное и невоспроизводимое поведенческое поведение позволяет легко ошибиться, как это было сделано здесь. –

5

sts::async возвращает специальный std::future. Это будущее имеет ~future, что делает .wait().

Итак, ваши примеры принципиально разные. Медленные фактически выполняют задачи во время вашего времени. Быстрые просто останавливают задачи и забывают, как когда-либо знать, что задача выполнена. Поскольку поведение программ, которые препятствуют потокам в прошлом прошлом, является непредсказуемым, его следует избегать.

Правильный путь для сравнения задач является сохранение в результате future когда genersting, и до того, как таймер заканчивается либо .wait()/.join() их всех, или избежать не уничтожая объекты только после истечения таймера. Однако в этом последнем случае шитье выглядит хуже, чем есть.

Вам нужно присоединиться/подождать, прежде чем начать следующий тест, так как иначе вы крадете ресурсы из своего времени.

Обратите внимание, что перемещенные фьючерсы удаляют ожидание от источника.

 Смежные вопросы

  • Нет связанных вопросов^_^