何时无法通过并行化来提高计算速度?

时间:2017-02-17 17:44:10

标签: multithreading multiprocessing

以前曾提出类似的问题,但我找不到更多关于线程本身的低级机制的答案。

问题

我有一个物理建模项目,我需要将一个函数应用于1600亿个数据点。

for(int i=0; i < N(160,000,000,000); i++){
     physicalModal(input[i]); //Linear function, just additions and subtractions
}
function physicalModal(x){
A*x +B*x +C*x + D*x......... //An over simplification but you get the idea. A linear function
}

考虑到这个问题的本质,我认为单个核心上的单个线程或每个核心1个线程是解决这个问题的最快方法吗?使用超出内核数量的额外线程对我没有帮助吗?

我的逻辑(请纠正我的假设错误的地方)

单个内核上的线程并不真正独立工作,它们只是共享处理器时间,当一个线程正在等待套接字响应并且其他线程正在处理请求时,这可能是有益的。在我上面发布的示例中,我认为CPU可以在一个线程上达到100%,因此使用多个线程只会干扰计算。这是对的吗?

什么决定线程何时有用?

如果我的上述假设是正确的,那么确定其他线程何时有用的关键因素是什么?我的猜测是同时进行的操作有不同的完成时间,等待等等......但这基于我最初的前提可能是不正确的。

1 个答案:

答案 0 :(得分:1)

  

我需要将函数应用于1600亿个数据点。

我假设你的函数没有副作用(没有写入全局/静态变量;没有磁盘/网络访问;没有许多远程用户的服务),只是在输入上做一些算术(在单点输入或几个stencil附近的点(stencil kernel}:

for(int i=0; i < 160_000_000_000; i++){
     //Linear function, just additions and subtractions 
     output[i] = physicalModel(input[i] /* possibly also input[i-1], input[i+1] .. */); 
}

然后你必须检查这个功能在单CPU上的效率。您(或您的编译器)unroll your loop可以将其转换为SIMD并行性吗?

for(int i=0+1; i < 160_000_000_000-1; i++){
    output[i] = A*input[i-1]+ B*input[i] + C*input[i+1];
}
// unrolled 4 times; if input is float, compiler may load 4 floats 
// into single SSE2 reg and do 4 operations from one asm command
for(int i=0+4; i < 160_000_000_000-4; i+=4){
    output[i+0] = A*input[i-1]+ B*input[i+0] + C*input[i+1];
    output[i+1] = A*input[i+0]+ B*input[i+1] + C*input[i+2];
    output[i+2] = A*input[i+1]+ B*input[i+2] + C*input[i+3];
    output[i+3] = A*input[i+2]+ B*input[i+3] + C*input[i+4];
}

当您的函数具有良好的单线程性能时,您可以添加线程或进程并行性(使用OpenMP / MPI或其他方法)。根据我们的假设,在某些外部资源上没有线程阻塞,例如从HDD或网络读取,因此您启动的每个线程都可以随时运行。然后我们应该为每个CPU核心启动不超过1个线程。如果我们启动多个线程,每个线程将运行一段时间并被其他线程取代,其性能低于每个cpu核心1个线程的情况。

在C / C ++中添加OpenMP线程级并行(https://en.wikipedia.org/wiki/OpenMPhttp://www.openmp.org/)就像在for循环之前添加一行一样简单(和adding -fopenmp/-openmp option到编译中) ;编译器和库将你的for循环分成几部分并在线程之间分配([0..N / 4],[N / 4..N / 2],[N / 2..N * 3/4],[ N * 3 / 4..N]用于4个线程或其他拆分方案;您可以使用schedule option提供提示

#pragma omp parallel for
for(int i=0+1; i < 160_000_000_000-1; i++){
    output[i] = physicalModel(input[i]);;
}

线程计数将在运行时由OpenMP库确定(gomp in gcc - https://gcc.gnu.org/onlinedocs/libgomp/index.html)。默认情况下it is "one thread per CPU is used"(每个逻辑cpu核心)。您可以使用OMP_NUM_THREADS环境变量(export OMP_NUM_THREADS=5; ./program)更改线程数。

在单cpu内核上具有硬件多线程的CPU上(Intel HT,SMT的其他变体:你有4个物理内核和8“逻辑”)在某些情况下你应该为每个逻辑内核使用1个线程,在其他情况下1个线程每个物理核心(具有正确的线程绑定),作为一些资源(FPU单元)are shared between logical cores。如果您的代码将被使用几次(很多次),请做一些实验。

如果您的线程(模型)受到内存速度的限制(内存绑定;它们从内存加载许多数据并在每个浮点数上执行非常简单的操作),您可能希望运行的线程少于cpu核心数,作为附加线程不会获得额外的内存带宽。

如果您的线程对从内存加载的每个元素进行大量计算,请使用更好的SIMD和更多线程(计算绑定)。当你有一个非常好的和宽的SIMD(全宽AVX)时,你将没有使用HT的加速,因为全宽AVX单元在逻辑核心之间共享(但每个物理核心都有一个,所以使用它);在这种情况下,你也会有较低的CPU频率,因为全宽AVX单元在满负荷下非常热。

内存和计算有限应用程序的说明:https://crd.lbl.gov/departments/computer-science/PAR/research/roofline/

https://crd.lbl.gov/assets/Uploads/FTG/Projects/Roofline/_resampled/ResizedImage600300-rooflineai.png