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. 关键部分当然是性能杀手,尤其是在计算不是太昂贵和/或 Tracks 的向量不是很大的情况下。
  2. 您存储 Track 对象的事实意味着,当您将它们从本地向量移动到最终向量时,您必须复制构造它们。
  3. 您知道向量的最终大小,但您可以动态地增长它们。
  4. threadBegin 和 threadEnd 函数利用浮点运算和 FP 到整数的转换。这些,尤其是转换,比执行等效的整数运算要慢得多。

这是我的建议:

  1. 将 std::unique_ptr 存储在向量中。
  2. 将您的向量预先分配到最终大小。
  3. 为了避免最后需要一个关键部分,我看到了两个选项:a)直接在最终数组中工作,但要找到正确的块。由于它会预先分配,因此您不必保护它。b) 在本地向量中工作,然后从线程内复制到预分配的最终向量的正确块。
  4. 使用整数数学计算您的块 - 您应该能够在分叉之前进行大部分计算,然后只需更正最后一个块的大小。
于 2012-08-23T11:59:20.570 回答