2016-05-21 31 views
18

私はを火災に使うべきだと言われました& std::launch::asyncパラメータのタスクのタイプを忘れてしまいました。(それで、新しい実行スレッドでは魔法が好きです)。これらのステートメントに励まさ std :: asyncは単純な分離スレッドと比較して遅いのはなぜですか?

は、私が比較される方法 std::async見てみたかった:

  • 順次実行
  • シンプル戸建std::thread
  • 私の単純な非同期 "実装"

マイ単純な非同期実装は次のようになります。

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; 
} 

ここ空想何も、その引数と一緒にstd::packaged taskにファンクタfパック、デタッチされた新しいstd::thread上でそれを起動し、タスクからstd::futureで返します。今

そしてstd::chrono::high_resolution_clockとコード計測実行時間:

void someTask() 
{ 
    std::this_thread::sleep_for(std::chrono::milliseconds(1)); 
} 

最後に、私の結果:someTask()が行われ、いくつかの仕事をシミュレートし、私は少し待っ簡単な方法を、ある

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; 
} 

  • 順次:1263615
  • スレッド:47111
  • のstd ::同期:821441
  • マイ非同期:30784

は誰は、これらの結果を説明してもらえますか? std::aysncのように思われます。私の純粋な実装よりも遅い、または単純明快で単純ながデタッチstd::thread sです。 なぜですか?これらの結果の後にstd::asyncを使用する理由がありますか?次のように私は私の小さなベンチマークを更新デイブSの答えを読んだ後

UPDATE(私が打ち鳴らす++とg ++、あまりにも、その結果は非常に類似していたと、このベンチマークをしたことに注意してください)

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はすべての実行で破棄されないため、結合されません。このコードを変更した後、std::asyncは私の実装と同じ結果をもたらします&はstd::threadをデタッチします。

+4

これは問題ではないと確信していますが、私は情報の完全性を求める必要があります。デバッグ(最適化されていない)またはリリース(最適化)されたビルドを測定していますか?私は最適化されたビルドを想定しています。それ以外の測定は無意味ですが、私は尋ねなければなりません。 –

+0

@JesperJuhl完全に有効な質問ですが、私は-O2で測定しています。 –

答えて

17

重要な違いの1つは、未来がasyncによって返され、未来が破壊されたとき、またはあなたのケースで新しい値で置き換えられたときにスレッドに参加するという点です。

つまり、someTask()を実行してスレッドに参加する必要がありますが、両方に時間がかかります。あなたの他のテストでは、それを独立して産み出すだけのテストはありません。

+5

"未来が破棄されたときにasyncによって返された未来はスレッドに加わります。*" AKA:決して使わない主な理由は、* std :: async'です。 –

+4

@NicolBolas:実際に操作をローカルで完了させない限り... –

+2

@KerrekSB:それを望むなら、他のすべての 'future'と同じように' future :: wait'を明示的に呼び出す必要があります。問題は、 'async'によって返される' future'が*他の 'future'オブジェクト*とは異なる振舞いを持つことです。それは問題であり、行動ではなく、不一致です。このように一貫性がなく、ユーザーが再現性のない動作をすると、誤って誤って行うのが非常に簡単になります。 –

5

sts::asyncは、std::futureを返します。この未来にはを持つ~futureがあります。

あなたの例は根本的に異なります。遅いものは実際にあなたのタイミングの間に仕事をします。速いものはタスクをキューに入れ、タスクが完了したことをいつまで知っているか忘れてしまいます。スレッドがメインの最後を過ぎるようにするプログラムの動作は予測できないので、回避する必要があります。

タスクを比較するための正しい方法はgenerstingと、得られるfutureを格納し、タイマーはどちらか.wait()/.join()それらをすべて終了する前、またはタイマーが満了するまでのオブジェクトを破壊しないようにすることです。しかし、この最後のケースでは、汚れたバージョンがそれより悪く見えます。

次のテストを開始する前に参加/待機する必要があります。そうしないと、タイミングからリソースを奪ってしまうからです。

移動先では、ソースから待機が削除されることに注意してください。

関連する問題