为什么这种令人尴尬的并行算法的性能没有随着多线程而提高

Why does the performance of this embarrassingly parallel algorithm not improve with multi-threading?

本文关键字:多线程 并行算法 为什么 性能      更新时间:2023-10-16

这是我在这里的第一篇文章,尽管我确实定期访问该网站,并在这里找到了很多有价值的信息。

我有一个令人尴尬的并行算法,我希望它能在多线程的情况下显示出巨大的性能改进。

经过大量的阅读和复习,这是我第一次使用多线程。

我使用的是带有VS 2012的C++,我的Windows 7笔记本电脑有一个四核i7处理器和充足的内存。

基本工作分解为这个伪代码

for (int i = 0; i<iMax; i++){
    for (int j = 0; j<jMax; j++){
        T[j] += E[j][i] * SF;
    }
}

T、 E和SF是浮子。

该实现从这里使用了一个(修改后的)线程池。

并从这个函数为线程池构建和添加一组任务

void doWork(float *T, float *E, float SF, int numNodes)
{
    // Critical for performance that these loops vectorize.....
    for (int nodeCounter = 0; nodeCounter < numNodes; nodeCounter++){
        T[nodeCounter] += E[nodeCounter] * SF;
    }
};

使用这种结构,

tp.enqueue(std::bind(&doWork, timeStepDisplacements.T1, T1MODE, T1MPF, numNodes));

在我的测试中,numNodes是1000000,我为50个外循环中的每一个调用这个例程3次(使用不同的数组)。我在它的外部还有另一个循环(100),所以我的测试代码生成了这些任务中的15000个,每个任务执行1000000次乘法运算。

编辑:将外循环计数更正为100,任务数从7500更正为15000

当我用8个、16个或更多线程设置线程池时,性能只比串行代码略好——比如8.8秒v's 9.3。

所以我的问题是,为什么性能改进这么小?

注意-如果使用不同的任务例程(下面的work_proc),则相同的线程池设置将显示出巨大的性能提升。

void work_proc()
{
    int i = 555;
    std::random_device rd;
    std::mt19937 rng(rd());
    // build a vector of random numbers
    std::vector<int> data;
    data.reserve(100000);
    std::generate_n(std::back_inserter(data), data.capacity(), [&](){ return rng(); });
    std::sort(data.begin(), data.end());
}

我发布整个代码没有问题,但我想我应该从这些关键部分开始。

提前Thanx了解所提供的任何见解。

您可能忽略了一些重要的部分,但如果您的伪代码是准确的,那么瓶颈似乎是内存访问。

单核可以以足够快的速度添加数字,从而使DRAM得到充分利用,因此通过拆分这些工作不会获得太多性能。

编辑:如果你知道你的DRAM类型和I/O时钟速率,你可以计算你的DRAM传输速率。这是关于它的速度吗?

例如:15000*1000000在9.3秒内浮动,读取速度为6.4 GB/s。如果你写的量相同,那么就是12.8 GB/s,这是你在评论中所说的DDR3-1600的最大速率。。。

这当然是你的问题。

请注意,您不应该真的需要写入相同的量,因此,如果您将算法重组为更友好的缓存,您可能会使其在您的盒子上的速度几乎是原来的两倍。

如果你让每个工人做4个E,比如:

T[nodeCounter] += (E1[nodeCounter] + E2[nodeCounter] + E3[nodeCounter] + E4[nodeCounter])*SF

那么这将显著降低你的T带宽,并使你非常接近最大速度。