多线程程序比单线程慢

西奥多·约翰逊

我创建了一个非常简单的测试程序,该程序可以计算某些标量c和矩阵A的c * A。您可以在此处在线运行它,也可以将以下代码粘贴到您喜欢的文本编辑器中:

#include <iostream>
#include <time.h>
#include <chrono>
#include <thread>

void fill_rand_matrix(double* mat, int n){
    
    for (int i=0;i<n;i++){
        mat[i]=static_cast <double> (rand()) / static_cast <double> (RAND_MAX)*20-10;
    }
}

void test(size_t m, size_t n, double alpha, double* X) {

        for (int j = 0; j < m; ++j) {
            for (int i = 0; i < n; ++i) {
                X[i+ j*n] *= alpha;
            }
        }       
}   

int main()
{
    int m=10000;
    int n=10000;
    double res_scaling=0.5;
    
    double* res=new double[m*n];
    fill_rand_matrix(res,n*m);
    
    auto begin1 = std::chrono::steady_clock::now();
    std::thread t1(test,0.5*m,n,res_scaling,res);
    std::thread t2(test,0.5*m,n,res_scaling,(double*)(res+(m/2)*n));
    t1.join();
    t2.join();
    auto end1= std::chrono::steady_clock::now();
    std::cout << "Time taken multithreaded = " << std::chrono::duration_cast<std::chrono::milliseconds>(end1 - begin1).count() << "[ms]" << std::endl;

    auto begin2 = std::chrono::steady_clock::now();
    test(m,n,res_scaling,res);
    auto end2= std::chrono::steady_clock::now();
    std::cout << "Time taken singlethreaded = " << std::chrono::duration_cast<std::chrono::milliseconds>(end2 - begin2).count() << "[ms]" << std::endl;

    return 0;
}

当我多次运行此代码时,多线程版本仅比单线程变体快一点,甚至更慢。如果我添加两个以上的线程,甚至会发生这种情况。即使该问题几乎可以随内核数量完美地扩展,多线程似乎也没有任何好处。此外,我设置的矩阵大小越高,运行时间的波动就越剧烈,有时多达20倍。

你知道这里发生了什么吗?

艾略特

我没有足够的知识来给出明确的答案,但是由于到目前为止还没有答案,我会尽力而为:


简短答案:

在大小为L的数组(其中L大于最大缓存的大小)上的多个顺序迭代将无法利用任何缓存来对数组进行新的缓存行访问(因为缓存使用了变体的LRU)。通过快速计算在大小为L的数组上快速迭代意味着访问(主)内存是瓶颈,并且将占用所有正在运行的进程/线程共享内存总线添加更多受主内存访问限制的线程只会导致它们之间的竞争。

(如果非常聪明,您的缓存可能会在将每个新数组数据传递到L2缓存之前就丢弃它们,这是因为您意识到在将其推出之前,您将无法使用它。这不会影响此过程,但会为其他人留下更多的缓存空间。)


更多信息:

对我来说, g++ -std=c++17 -O3 -pthread -S -fverbose-asm

...为汇编输出提供movups与该行两次关联的指令:

X[i+ j*n] *= alpha; // line 17

movupsx86并行化(SIMD)指令这样的SIMD指令通常将4 doubles放入一个巨大的寄存器中,以非常快速地进行计算(总共约10个时钟周期,但如果我错,请纠正我)。将此乘以4,即可在高速缓存行上完成工作:约40个周期。如果您不使用x86,则可能是使用具有类似并行化指令的CPU。

主内存访问速度很慢(从主内存中获取高速缓存行大约需要100-200个时钟周期[高速缓存行= 64字节的块〜= 16倍)]。

你的CPU将尝试预取数据,以帮助你,但是,因为你总是以更快的速度比数据总线可以提供请求从主内存中的数据,然后预取可能不仅有助于减少您的等待时间从说〜100至〜60,如果您幸运的话。无论哪种方式,等待主内存访问仍然是瓶颈。

请注意,这也可以从另一个方向进行,您可以使用更新的数组值填充缓存,但是在8MB左右之后,您将需要不断地将该数据发送回主内存。因此,实际上我们的上述估计是乐观的。


Nitpick:

test函数有一个小错误:

j < m并且i < n是单/无符号比较。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

多线程程序的运行速度比单线程程序慢

来自分类Dev

多线程图像处理比单线程慢?

来自分类Dev

为什么我的多线程比单线程慢?

来自分类Dev

多线程图像处理比单线程慢?

来自分类Dev

ruby 中的多线程比单线程慢

来自分类Dev

单线程CPU上的多线程应用程序?

来自分类Dev

同步多线程与单线程

来自分类Dev

与单线程相比,多线程更快

来自分类Dev

多线程比单线程快吗?

来自分类Dev

C ++:线程池比单线程慢?

来自分类Dev

具有大量内存访问功能的多线程比单线程慢

来自分类Dev

javascript web workers 多线程字符串搜索比单线程慢?

来自分类Dev

Python多处理比单线程慢

来自分类Dev

Dijkstra算法OpenMP比单线程慢

来自分类Dev

Python多处理比单线程慢

来自分类常见问题

多线程程序花费的时间比单线程(Java)长

来自分类Dev

如何在Visual Studio中将多线程应用程序调试为单线程

来自分类Dev

单线程与多线程JMS生产者

来自分类Dev

Java是默认的单线程还是多线程?

来自分类Dev

Java是默认的单线程还是多线程?

来自分类Dev

C ++多线程代码与单线程同时执行

来自分类Dev

使用多线程程序而不是使用单线程程序并行编译项目是否具有性能优势?

来自分类Dev

在C ++(VC ++ 2010 Express)上,双线程应用程序比单线程应用程序慢。怎么解决?

来自分类Dev

Akka-Stream实现比单线程实现慢

来自分类Dev

将单线程应用程序迁移到多线程,并行执行,蒙特卡洛仿真

来自分类Dev

避免单线程死锁

来自分类Dev

Javascript是如何单线程的?

来自分类Dev

避免单线程死锁

来自分类Dev

单线程中断睡眠