2012-05-29 32 views
19

C++ 2011包含非常酷的新功能,但我找不到很多示例來並行化for-loop。 所以我非常天真的問題是:你如何爲std :: thread並行化一個簡單的for循環(比如使用「omp parallel for」)? (我搜索一個例子)。C++ 2011:std :: thread:並行化循環的簡單示例?

非常感謝。

+1

for循環中是否有任何關鍵部分? –

+4

我越看越OMG,我認爲std :: thread並不是要取代它。 'std :: thread'並不意味着優化低級別的CRUD操作。 –

+4

你可以發佈一個你試圖並行化的循環嗎? –

回答

29

std::thread不一定意味着平行循環。它意味着構建像parallel_for算法這樣的構造的低級抽象。如果你想平滑你的循環,你應該自己或者使用一個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模板庫,它可以簡化爲

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() 

,並在你的主代碼,你可以在兩個塊處理數組:

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

AFAIK最簡單的方式並行化循環,如果你確信沒有併發訪問可能是通過使用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 ++,這樣做將使用Lambda和for_each的,並且使用GNU並行擴展(可使用OpenMP的幕後)的另一個非常C++ 11上下的方式:

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

然而,for_each主要是爲數組,向量等考慮的...... 但是如果你只想通過創建一個Range類的beginend方法來遍歷一個範圍,那麼你可以「欺騙」它。

注意,對於簡單的循環,即做數學的東西,在#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 ::螺紋和lambda表達式定義宏:

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

它的醜陋,但有用。