1

Cilk Plus を使用して並列化したいシリアル コードがあります。ntsメイン ループは、異なるデータ セットに対して処理関数を繰り返し呼び出すため、外部ライブラリによって提供されるクラス (たとえば ) にカプセル化された非スレッド セーフ リソースの使用を除いて、反復は互いに独立しています。これはファイル名を取り、それに対して I/O を行います。

OpenMP を使用している場合は、スレッドと同じ数のリソースを含むリソースのプールを作成し、スレッド ID に従ってこれらのリソースにアクセスします。

std::vector<nts> nts_pool;
for (std::size_t i{0}; i < omp_get_num_threads(); ++i)
    nts_pool.push_back(nts{});

nts_pool[omp_get_thread_num()].do_stuff();  // from inside the task

Cilk Plus を使用すると、 API__cilkrts_get_nworkers()__cilkrts_get_worker_number()API を使用して同様のことができますが、Intel フォーラムの複数の投稿から、これは問題に対する間違った解決策であると考えられており、正しい解決策はホルダー ハイパーオブジェクトを使用することであることがわかりました。

ワーカー スレッドと同じ数のビューだけを作成したいという点を除けば、ホルダー ソリューションは確かに見栄えがします。つまり、3 つのワーカー スレッドの場合、3 つのオブジェクトが必要であり、それ以上は必要ありません。正当な理由は、私が言うように、リソースはサードパーティのライブラリによって提供され、構築するのに非常に費用がかかり、後で結果のファイルを処理する必要があるため、少ないほど良いということです.

残念ながら、ワーカーごとにビューを作成して同期まで保持するのではなく、所有者が私が理解できないロジックに従ってビューを作成および破棄していることを発見しました。影響を与える方法はないようです。この動作。

ホルダーを私が望むように動作させることは可能ですか? そうでない場合、私の問題に対する慣用的な Cilk Plus ソリューションは何でしょうか?

ホルダーを調査するために使用したプログラムは次のとおりです。1 回の実行でテスト マシンに最大 50 個のビューが作成され、一見ランダムに割り当てられて破棄されることに注意してください。

#include <iostream>
#include <atomic>

#include <cilk/cilk.h>
#include <cilk/holder.h>
#include <cilk/reducer_ostream.h>
#include <cilk/cilk_api.h>

cilk::reducer_ostream *hyper_cout;

class nts {
public:
    nts() : tag_{std::to_string(++id_)} {
        *hyper_cout << "NTS constructor: " << tag_ << std::endl;
    }
    ~nts() {
        *hyper_cout << "NTS destructor: " << tag_ << std::endl;
    }
    void print_tag() {
        *hyper_cout << "NTS tag: " << tag_ << std::endl;
    }
    static void is_lock_free() {
        *hyper_cout << "Atomic is lockfree: " << id_.is_lock_free() << std::endl;
    }
private:
    const std::string tag_;
    static std::atomic_size_t id_;
};

std::atomic_size_t nts::id_{0};

class nts_holder {
public:
    void print_tag() { nts_().print_tag(); }
private:
    cilk::holder<nts> nts_;
};

int main() {

    __cilkrts_set_param("nworkers", "4");

    cilk::reducer_ostream cout{std::cout};
    hyper_cout = &cout;

    *hyper_cout << "Workers: " <<  __cilkrts_get_nworkers() << std::endl;
    nts::is_lock_free();

    nts_holder ntsh;
    ntsh.print_tag();

    for (std::size_t i{0}; i < 1000; ++i) {
        cilk_spawn [&] () {
            ntsh.print_tag();
        } ();
    }

    cilk_sync;

    return 0;

}
4

1 に答える 1

1

ホルダーは魅力的ですが、この特定の問題に対する非効率的な解決策であることは間違いありません。ワーカーごとに 1 つのスロットを持つスロットの配列を使用してプログラムが正しい場合、この場合に__cilkrts_get_nworkers()および__cilkrts_get_worker_number()API を使用しても問題はありません。一般に、それらの使用はお勧めしません。ワーカー数に気付かない Cilk Plus コードを作成することを好みます。ただし、これを含め、ワーカーごとにスロットを作成することが最善の戦略である場合もあります。

于 2015-07-21T16:00:11.960 に答える