如何改进此代码以便在多线程环境下运行

How to improve this code in order to be run multithreaded?

本文关键字:多线程 环境 运行 何改进 代码      更新时间:2023-10-16

我刚刚开始使用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),以防这可能是原因。

第页。S.我的第一个版本在关键部分使用了list.push_back(..)的并行for循环,这甚至比这里发布的变体还要慢。

好吧,您的代码似乎是正确的,但我看到的可能是性能问题:

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

以下是我的建议:

  1. 在矢量中存储std::unique_ptr
  2. 将矢量预先分配到最终大小
  3. 为了避免最后需要一个关键部分,我有两种选择:a) 直接在最终数组中工作,但要找到正确的块。由于它是预先分配的,所以您不必保护它。b) 在局部向量中工作,然后从线程内复制到预分配的最终向量的正确块
  4. 使用整数数学计算你的区块——你应该能够在分叉之前完成大部分计算,然后只需更正最后一个区块的大小