gpt4 book ai didi

c++ - 多线程程序比单线程程序慢

转载 作者:行者123 更新时间:2023-12-03 06:54:24 24 4
gpt4 key购买 nike

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

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

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

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

最佳答案

我的知识不足以给出明确的答案,但由于目前还没有答案,我会尽力而为:


简答:

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

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


更多信息:

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

...用 movups 给出汇编输出与该行相关联的指令两次:

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

movupsx86 parallelisation (SIMD) instruction .像这样的 SIMD 指令通常把 4 double s 到一个巨大的寄存器上以非常快速地进行计算( ~10 clock cycles all up ,但如果我错了请纠正我)。将其乘以 4 得到在缓存行上完成的工作:~40 个周期。如果您不使用 x86,那么您可能正在使用具有类似并行化指令的 CPU。

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

但是,您的 CPU 会尝试通过预取数据来帮助您,因为您总是以比数据总线更快的速率从主内存请求数据如果幸运的话,预取可能只会帮助您将等待时间从大约 100 分钟减少到 60 分钟。无论哪种方式,等待主内存访问仍然是瓶颈。

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


挑剔:

test函数有一个小错误:

j < mi < n是有符号/无符号的比较。

关于c++ - 多线程程序比单线程程序慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64607939/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com