2

私はこれを正しく測定していないかもしれませんが、私が遊んでいるいくつかの簡単なコードがあります。スレッドプールではないと思います。ワークユニットを非常に大きくすると、CPUは190〜199%になります(デュアルコアを使用しています)が、低くすると、ワークユニットは小さくなりますが、CPUよりもはるかに多くなります。プログラムを140〜160%で実行します。

私が考えているのは、スレッドはプールされていませんが、必要に応じて破棄/作成されているため、ワークロードが小さい場合はプログラムの速度が低下します。スレッドの数を制御するために使用tbb::task_scheduler_initしていますが、スレッドを存続させるようにtbbに指示する方法がわかりません。

問題を説明するためのコードを次に示します。

#include <iostream>
#include <list>
#include <tbb/task.h>
#include <tbb/task_group.h>
//#include <stdlib.h>
#include "tbb/task_scheduler_init.h"
#include <boost/thread.hpp>

using namespace tbb;


long fib(long a)
{
    if (a < 2) return 1;
    
    return fib(a - 1) + fib(a - 2);
}

class PrintTask
{
public:
    void operator()()
    {
        //std::cout << "hi world!: " <<  boost::this_thread::get_id() << std::endl;
        
        fib(10);
    }
};

int main()
{
    tbb::task_scheduler_init init(4); //creates 4 threads
    task_group group;
    
    
    for (int i = 0; i < 2000000; ++i)
    {
        group.run(PrintTask());
        //std::cout << i << std::endl;
    }
    
    std::cout << "done" << std::endl;
    group.wait();
    
    return(0);
}

fibを40-45に変更すると、各スレッドの作業が大きくなり、フルスピードになりますが、現在の設定を使用すると、ジョブは非常に小さくなりますが、多くのことを実行します。

注:おそらく関連していることに気付いたのは、上記の場合、メモリを完全に使用していることです(4ギガ無料)。減速はそれに関連しているのでしょうか?また、なぜこのプログラムはすべてのメモリを使用するのでしょうか?スレッドを呼び出しているだけの場合、それをメモリに保存するのは何ですか?実行する回数を示すキューがありませんか、それとも実行するためにスレッド全体をメモリに保存していますか?

チュートリアルを読みましたが、それでもその動作に混乱しています(意図した結果は得られますが)。

4

2 に答える 2

4

CPU使用率についてはそれほど心配する必要はありませんが、実行時間とスピードアップとシーケンシャルを比較してください。

tbbについて理解する必要があることがいくつかあります。

タスクのスケジューリングのオーバーヘッドは、基本的に一定であると考えることができます(完全ではありませんが、十分に近いです)。スケジュールする作業量が少ないほど、この定数に近づくことになります。この定数に近づくと、スピードアップは見られません。

また、スレッドは作業が見つからないときにアイドル状態になります。'fib(10)'を呼び出すと、runの呼び出しコストと必要なタスク割り当てが、実際に作業を実行するコストに近づいていると思います。

より具体的には、同じタスクで2000000個のアイテムが実際にある場合は、おそらくparallel_forまたはparallel_for_eachを呼び出す必要があります。

-リック

于 2012-05-15T17:34:20.210 に答える
2

リックの最後のポイントを強調するために、状態のドキュメントtask_group

注意:タスクの作成はシリアルのボトルネックになるため、1つのtask_groupに対して多数のタスクを作成することはスケーラブルではありません。少数を超える同時タスクを作成する場合は、代わりにparallel_for(4.4)またはparallel_invoke(4.12)を使用するか、スポーンを再帰ツリーとして構造化することを検討してください。

私は実際に、parallel_doタスクの数がわからないという事前のユースケースに当てはまる可能性が高いものとして、そのリストに追加します。その引数には、さらにタスクを追加できる「フィーダー」が含まれています。ただし、ドキュメント内のコメントは、一度に1つずつタスクを処理していないことに注意してください。選択的引用:

ボディが複数の作業を追加することが多いように、アルゴリズムを設計します。...高速化を実現するには、B :: operator()の粒度が少なくとも約100,000クロックサイクルのオーダーである必要があります。それ以外の場合、parallel_doの内部オーバーヘッドが有用な作業を圧倒します。

自明でないTBBの例を探している場合(チュートリアルparallel_forなどを超えて)、TBBパターンのマニュアルは非常に優れています。

于 2012-05-15T19:37:01.520 に答える