为什么openMP / MPI分布式进程比MATLAB慢?

时间:2017-05-01 13:42:25

标签: matlab multiprocessing openmp distributed-computing openmpi

我编写了一个混合的OpenMP / MPI程序,它基本上通过MPI将for(){...}循环的迭代分发到非共享内存系统。在一台机器中,我使用静态计划专门调用openMP。

我的代码抽象地如下所示:

#pragma omp parallel for schedule(static) collapse(2) nowait
for(j=1;j>-2;j-=2){
    for(i=0;i<n;i++){
        ...                    // nested loop code here
    }
}

我将MATLAB上完全相同的代码段的运行时间与parfor循环进行了比较,并且使用C代码使运行时间始终减慢了30%。

我希望C代码的运行时间至少相等。

我通过shell函数time监视运行时间,如下所示

time matlab script.m

time mpirun -np 1 --bind-to none -x OMP_NUM_THREADS=32 ./script

我正在使用openmp 3.1和gcc 4.7.3以及openMPI v1.10.3

我在openMPI中使用--bind-to none选项调用该程序,为openMP调用OMP_PROC_BIND=TRUE

有什么想法会发生这种情况吗?

修改

假设32个线程,在MATLAB中,循环如下所示:

parfor k=1:nWorkers
    for j=[-1,1]
       for i=1:n
           ...                 % nested loop here
       end
    end
end

2 个答案:

答案 0 :(得分:1)

你的并列化循环是 for(j=1;j>-2;j-=2) ,它只是j=1j=-1。因此,您只能获得两个执行n循环的线程。我可以想象你在MATLAB中做了其他的事情,但你没有提供任何代码,所以我不能说你的MATLAB代码。

此外,您正在将MPI(只有一个线程)与openMP结合使用,您确定这是您正在寻找的吗?

答案 1 :(得分:1)

TL; DR; - 下面列出了MATLAB和C-lang的最佳实践。

(进入HPC和分布式计算领域需要一些新的自律,因为玩具变得越来越复杂,如果纯粹依赖于我们之前的纯粹,网络效应不容易解构为各自的根本原因[SERIAL]来自常见编程语言的调度经验。)

永远不要使用shell time来认真衡量/比较性能:

一旦分布式并发进程达到大量openMP线程/ MPI进程,在Amdahl's Law denominator中设置除数 N 就越多。

                                1
processSPEEDUP = _______________________________
                               ( 1 - SEQ_part )     <---- CONCURRENCY MAY HELP
                  SEQ_part +  _________________
                  ^^^                 N             <---- CONCURRENCY HARNESSED
                  |||
                  SEQ____________________________________ CONCURRENCY IMMUNE PART

预期应该是现实的:

enter image description here

永远不要指望SPEEDUP晚餐是免费的,因为Amdahl's Law似乎已承诺

永远不要使用shell时间来认真测量/比较性能:

一旦分布式并发进程达到大量openMP线程/ MPI进程,在Amdahl定律分母中设置除数 N 就越多。

                               1
processSPEEDUP = ___________________________________
                             ( 1 - SEQ_part )        <-- CONCURRENCY MAY HELP
                 SEQ_part + _________________ + CoST 
                 ^^^                 N          ^^^
                 |||                 ^          |||
                 |||                 |          |||        
                 |||                 +------------------ CONCURRENCY HARNESSED
                 |||                            |||
                 |||                            |||      A GAIN WITHOUT PAIN?
                 |||                            |||      
                 |||                            |||      NEVER, SORRY,
                 |||                            |||      COMMUNISM DOES NOT WORK,
                 |||                            |||      ALL GOT AT THE COST OF
                 |||                            +++----- COSTS-OF-ALL-OVERHEADS
                 |||                                     ++++++++++++++++++++++
                 |||                                     +N job SETUPs
                 |||                                     +N job DISTRIBUTIONs
                 |||                                     +N job COLLECT RESULTs
                 |||                                     +N job TERMINATIONs
                 |||
                 |||
                 SEQ____________________________________ CONCURRENCY IMMUNE PART

For more details on the impacts from add-on costs-of-overheads, may like to read this,或者可以直接进入一个交互式GUI工具,用于基于现实,量化的基于现实的插图,说明任何数量的CPU将如何加速昂贵“分布式作业,在this post, where SPEEDUPs are shown
WHY they actually turn out to become SLOWDOWNs, on any amount of N
Q.E.D.
的预告片部分中引用。

另外还有一些关于实际约束的更多细节(与线程模型相关,与硬件域相关,特定于NUMA),它们共同决定了所产生的调度以及这种声明的执行流程的可实现的加速:

enter image description here

独立于细节如何通过分布式处理并发加速进程,此处讨论了测量质量。

C中的进程应始终使用:

系统中可用的任何高分辨率计时器。作为快速模拟的例子,可以:

#include <time.h>

struct timespec {
                 time_t tv_sec;  /* seconds */
                 long   tv_nsec; /* nanoseconds */
       };

timespec diff( timespec start, timespec end ) {
         timespec temp;
         if ( ( end.tv_nsec - start.tv_nsec ) <  0 ) {
               temp.tv_sec  = end.tv_sec  - start.tv_sec  - 1;
               temp.tv_nsec = end.tv_nsec - start.tv_nsec + 1000000000;
         } else {
               temp.tv_sec  = end.tv_sec  - start.tv_sec;
               temp.tv_nsec = end.tv_nsec - start.tv_nsec;
         }
         return temp;
       }

// /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
       struct timespec                           start_ts, end_ts, duration_ts;
       clock_gettime( CLOCK_THREAD_CPUTIME_ID,  &start_ts );
//     clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &start_ts );
//     clock_gettime( CLOCK_MONOTONIC,          &start_ts );
// ____MEASURED-SECTION_START____________________
                ...
                ..
                .
// ____MEASURED-SECTION_END______________________
       clock_gettime( CLOCK_THREAD_CPUTIME_ID,  &end_ts );
//     clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &end_ts );
//     clock_gettime( CLOCK_MONOTONIC,          &end_ts );
        // _____SECTION__________
        //         duration_ts = diff( start_ts, end_ts );
// \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/

MATLAB中的进程应始终使用tic; ...; delta = toc;

MathWorks技术文章对此主题的论述非常明确:

  

总之,使用tictoc来测量MATLAB中的经过时间,因为这些函数具有最高的准确性和最可预测的行为。基本语法是

tic;
    ... % … computation …
    ..
    .
toc;
  

MATLAB识别tictoc行以获得最小开销。