2012-05-29 27 views
19

C++ 2011には非常にクールな新機能が含まれていますが、forループを並列化するための多くの例は見つかりません。 私の非常に素朴な質問は、単純なforループ( "omp parallel for"をstd :: threadと同じように)をどのように並列化するのですか? (私は例を探します)。C++ 2011:std :: thread:ループを並列化する簡単な例?

ありがとうございました。

+1

forループに重要なセクションはありますか? –

+4

OMGをもっと見ると、std :: threadはそれを置き換えるものではないと思います。 'std :: thread'は、低レベルのCRUD操作を最適化するためのものではありません。 –

+4

並列化しようとしているループを投稿できますか? –

答えて

29

std::threadは必ずしもループを並列化する必要はありません。これはparallel_forアルゴリズムのような構造を構築するための低レベルの抽象化を意図しています。ループを並列化したい場合は、parallel_forアルゴリズムを自分で実行するか、タスクベースの擬似を提供する既存のlibraireを使用する必要があります。

次の例は、単純なループを並列化する方法を示していますが、反対側には欠落しているロードバランシングや単純なループの複雑さなどの欠点も示しています。parallel_forテンプレートを提供していますライブラリを使用して

typedef std::vector<int> container; 
    typedef container::iterator iter; 

    container v(100, 1); 

    auto worker = [] (iter begin, iter end) { 
    for(auto it = begin; it != end; ++it) { 
     *it *= 2; 
    } 
    }; 


    // serial 
    worker(std::begin(v), std::end(v)); 

    std::cout << std::accumulate(std::begin(v), std::end(v), 0) << std::endl; // 200 

    // parallel 
    std::vector<std::thread> threads(8); 
    const int grainsize = v.size()/8; 

    auto work_iter = std::begin(v); 
    for(auto it = std::begin(threads); it != std::end(threads) - 1; ++it) { 
    *it = std::thread(worker, work_iter, work_iter + grainsize); 
    work_iter += grainsize; 
    } 
    threads.back() = std::thread(worker, work_iter, std::end(v)); 

    for(auto&& i : threads) { 
    i.join(); 
    } 

    std::cout << std::accumulate(std::begin(v), std::end(v), 0) << std::endl; // 400 

、あなたが可能同時アクセスなしがないことを確認している場合、ある、ループを並列化する最も簡単な方法は私の知る限り

parallel_for(std::begin(v), std::end(v), worker); 
3

私たちは依然としてpthreadを主に使用しているので、C++ 11固有の答えは提供できません。しかし、言語にとらわれない答えとして、別の関数(スレッド関数)で実行するように設定して並列化します。言い換えれば

、あなたのような機能を持っている:

def processArraySegment (threadData): 
    arrayAddr = threadData->arrayAddr 
    startIdx = threadData->startIdx 
    endIdx = threadData->endIdx 

    for i = startIdx to endIdx: 
     doSomethingWith (arrayAddr[i]) 

    exitThread() 

と、メインのコードで、あなたは2つのチャンクに配列を処理することができます。

int xyzzy[100] 

threadData->arrayAddr = xyzzy 
threadData->startIdx = 0 
threadData->endIdx = 49 
threadData->done  = false 
tid1 = startThread (processArraySegment, threadData) 

// caveat coder: see below. 
threadData->arrayAddr = xyzzy 
threadData->startIdx = 50 
threadData->endIdx = 99 
threadData->done  = false 
tid2 = startThread (processArraySegment, threadData) 

waitForThreadExit (tid1) 
waitForThreadExit (tid2) 

(心の中で警告を保ちますスレッド1がローカルストレージにデータをロードしていることを確認する必要がありますメインスレッドはスレッド2、おそらくはミューテックスまたはの配列の構造スレッドごと)。それは、並列に実行されるようにそれがいいだろうけれどもつまり

が、それは、めったにだけforループを変更するだけの簡単な作業ですしない、のようなもの:

for {threads=10} ({i} = 0; {i} < ARR_SZ; {i}++) 
    array[{i}] = array[{i}] + 1; 

は代わりに、再配置のビットを必要としますスレッドを利用するコード

もちろん、データを並行して処理することが理にかなっていることを確認する必要があります。各配列要素を前の配列要素に1を加えたものに設定している場合、前の要素が最初に変更されるのを待たなければならないため、並列処理は必要ありません。

上記の特定の例では、スレッド関数に渡される引数を使用して、配列のどの部分を処理するかを指定しています。スレッド関数自体には、作業を行うループが含まれています。

4

明らかに、ループの機能、パラレル化の選択方法、スレッドの有効期間の管理方法によって異なります。

私はthe book from the std C++11 threading libraryを読んでいる(それはまたboost.threadメンテナの一つであり、Just Threadを書いた)、私は「それが依存」ことがわかります。

新しい標準のスレッドを使用して基礎を理解するために、たくさんの例があるので、この本を読むことをお勧めします。 また、http://www.justsoftwaresolutions.co.uk/threading/https://stackoverflow.com/questions/415994/boost-thread-tutorials

1

に単純化することができますOpenMPを使用します。

これは、LLVMを除くすべての主要コンパイラでサポートされています(2013年8月現在)。

例:

for(int i = 0; i < n; ++i) 
{ 
    tab[i] *= 2; 
    tab2[i] /= 2; 
    tab3[i] += tab[i] - tab2[i]; 
} 

これは、このように非常に簡単に並列化することになります。

#pragma omp parallel for 
for(int i = 0; i < n; ++i) 
{ 
    tab[i] *= 2; 
    tab2[i] /= 2; 
    tab3[i] += tab[i] - tab2[i]; 
} 

しかし、これは、値の大きな数でのみ効率的であることに注意してください。

あなたがg ++、ラムダとfor_eachを使用して、および(シーンの後ろのOpenMPを使用することができます)、GNU並列拡張機能を使用することだろうを行うための別の非常にC++ 11-っぽい道を使用する場合:

__gnu_parallel::for_each(std::begin(tab), std::end(tab), [&]() 
{ 
    stuff_of_your_loop(); 
}); 

しかし、for_eachは主に配列やベクトルなどのために考えられます。 Rangeクラスとbeginクラスとendメソッドを作成することで範囲を繰り返し処理したい場合は、主にintをインクリメントします。

数学的な処理を行う単純なループでは、#include <numeric>#include <algorithm>のアルゴリズムをすべてG ++と並列化することができます。

3

あなたがそれを行うことができthisクラスを使用するよう:

Range based loop (read and write) 
pforeach(auto &val, container) { 
    val = sin(val); 
}; 

Index based for-loop 
auto new_container = container; 
pfor(size_t i, 0, container.size()) { 
    new_container[i] = sin(container[i]); 
}; 
+0

リンクが再び機能します。 –

+0

このクラスは利用できません(404)。どのクラスを使用するかを詳しく教えてください。 – BernhardWebstudio

0

のstd ::スレッドとラムダ式を使用してマクロ定義:

#ifndef PARALLEL_FOR 
#define PARALLEL_FOR(INT_LOOP_BEGIN_INCLUSIVE, INT_LOOP_END_EXCLUSIVE,I,O)   \                \ 
    {                    \ 
     int LOOP_LIMIT=INT_LOOP_END_EXCLUSIVE-INT_LOOP_BEGIN_INCLUSIVE;    \ 
     std::thread threads[LOOP_LIMIT]; auto fParallelLoop=[&](int I){ O; };  \ 
     for(int i=0; i<LOOP_LIMIT; i++)            \ 
     {                   \ 
      threads[i]=std::thread(fParallelLoop,i+INT_LOOP_BEGIN_INCLUSIVE);  \ 
     }                   \ 
     for(int i=0; i<LOOP_LIMIT; i++)            \ 
     {                   \ 
      threads[i].join();              \ 
     }                   \ 
    }                    \ 
#endif 

用法:

int aaa=0; 
PARALLEL_FOR(0,90,i, 
{ 
    aaa+=i; 
}); 

その醜いが、できます。

関連する問題