2012-08-23 48 views
0

我剛開始使用OpenMP指令來使用多線程。然而,這段代碼使用單線程版本運行速度最快。在我看來,由於計算是獨立的,算法應該很好地縮放。這裏發生了什麼事?我如何改進代碼?如何改進此代碼才能運行多線程?

#include <omp.h> 

std::vector<Track> interpolateTracks(const std::vector<Track>& tracks, double segmentLength) { 
    typedef std::vector<Track>::const_iterator iterator; 
    std::vector<Track> list; 
    #pragma omp parallel shared(list, tracks, segmentLength) 
    { 
     std::vector<Track> local; 
     iterator myBegin = threadBegin(tracks.begin(), tracks.end()); 
     iterator myEnd = threadEnd(tracks.begin(), tracks.end()); 
     for (iterator i = myBegin; i < myEnd; ++i) { 
      const Track& t = *i; 
      TrackInterpolator interpol(t); 
      const Track& result = interpol.bySegmentLength(segmentLength); 
      local.push_back(result); 
     } 
     #pragma omp critical 
     { 
      list.insert(list.end(), local.begin(), local.end()); 
      std::cout << "Done: " << omp_get_thread_num() << std::endl; 
     } 
    } 
    return list; 
} 

功能beginThread(begin, end)和根據當前線程數目和線程的數目由beginend定義的範圍endThread(begin,end)返回小塊。

這裏的履行情況:

#include <omp.h> 

template <class I> 
I threadBegin(I begin, I end) { 
    int part = omp_get_thread_num(); 
    int parts = omp_get_num_threads(); 
    double chunk = (end - begin)*1.0/parts; 
    ptrdiff_t diff = (ptrdiff_t) (chunk*part); 
    return begin + diff; 
} 

template <class I> 
I threadEnd(I begin, I end) { 
    //the end of i is the begin of i+1 
    int part = omp_get_thread_num() + 1; 
    int parts = omp_get_num_threads(); 
    if (part == parts) { 
     return end; 
    } else { 
     double chunk = (end - begin)*1.0/parts; 
     ptrdiff_t diff = (ptrdiff_t) (chunk*part); 
     return begin + diff; 
    } 
} 

我運行在Linux機器上的代碼有16個內核。

不幸的是,我只能訪問過時的gcc((SUSE Linux)4.5.1 20101208),以防萬一這可能是原因。

P.S.我的第一個版本使用並行for循環與list.push_back(..)關鍵部分,這比在此處發佈的變體更慢。

+0

嗯,這是創建OpenMP的一種創造性方式,但最大的問題是 - 'threadBegin'和'threadEnd'函數是什麼樣子的? –

+0

哪一段代碼是最耗時的? 'interpol.bySegmentLength(segmentLength)'裏面發生了什麼;'? – Tudor

+0

我剛剛添加了實現。 'interpol.bySegmentLength(segmentLength);'和'TrackInterpolator interpol(t);'應該是最耗時的調用。 – Sebastian

回答

1

好了,你的代碼似乎是正確的,但這裏是我看到的可能性能問題:

  1. 的關鍵部分是,當然是性能殺手,特別是如果計算是不是太昂貴和/或者Tracks的矢量不是很大。
  2. 您存儲Track對象的事實意味着您必須在將它們從本地向量移動到最終向量時進行復制構建。
  3. 你知道你的載體的最終大小,但你動態地增長它們。
  4. threadBegin和threadEnd函數利用浮點運算和FP到整數轉換。這些,尤其是轉換,執行等效的整數操作要慢得多。

這裏是我的建議:

  1. 商店的std ::您的載體的unique_ptr。
  2. 預先分配你的載體到最終尺寸。
  3. 爲了避免在最後需要關鍵部分,我看到兩個選項: a)直接在最終數組中工作,但找到正確的塊。既然它會預先分配,你不必保護它。 b)在本地向量中工作,但是然後從線程內複製到預先分配的最終向量的正確塊。
  4. 使用整數數學計算您的塊 - 您應該能夠在分叉之前執行大部分計算,然後只需更正最後一個塊的大小。
+0

謝謝您的建議。我試過2.,3.我會很快發佈代碼。無論如何,它並沒有解決問題,儘管我完全避免了創建新的媒介。我做不到1.因爲它會造成太多的變化。 4.在新變體中不需要。無論如何,我認爲分區並沒有引起問題。 – Sebastian