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)と関数は、現在のスレッド番号とスレッド数によって定義されendThread(begin,end)た範囲の小さなチャンクを返します。beginend

それらの実装は次のとおりです。

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

16 コアの Linux マシンでコードを実行しています。

残念ながら、これが原因である可能性がある場合に備えて、少し古い gcc ((SUSE Linux) 4.5.1 20101208) にしかアクセスできません。

list.push_back(..)PS 私の最初のバージョンでは、クリティカルセクションで並列 for ループを使用していましたが、ここに投稿されたバリアントよりもさらに低速でした。

4

1 に答える 1

1

さて、あなたのコードは正しいようですが、私が見る可能性のあるパフォーマンスの問題は次のとおりです。

  1. もちろん、クリティカルセクションは、特に計算がそれほど高価ではない場合や、トラックのベクトルがそれほど大きくない場合は、パフォーマンスを低下させます。
  2. Trackオブジェクトを保存しているという事実は、ローカルベクトルから最終的なベクトルに移動するときに、それらをコピーして構築する必要があることを意味します。
  3. あなたはあなたのベクトルの最終的なサイズを知っています、それでもあなたはそれらを動的に成長させます。
  4. threadBegin関数とthreadEnd関数は、浮動小数点演算とFPから整数への変換を利用します。これら、特に変換は、同等の整数演算を実行するよりもはるかに低速です。

これが私が提案するものです:

  1. std::unique_ptrをベクターに保存します。
  2. ベクトルを最終的なサイズに事前に割り当てます。
  3. 最後にクリティカルセクションが必要になるのを避けるために、2つのオプションがあります。a)最終的な配列で直接作業しますが、正しいチャンクを見つけます。事前に割り当てられるため、保護する必要はありません。b)ローカルベクトルで作業しますが、スレッド内から事前に割り当てられた最終ベクトルの正しいチャンクにコピーします。
  4. 整数演算を使用してチャンクを計算します。フォークする前にほとんどの計算を実行でき、最後のチャンクのサイズを修正するだけです。
于 2012-08-23T11:59:20.570 に答える