为什么Amdahl Law在串行和并行分数上不能在四核CPU上提供4的理论加速?

时间:2017-01-09 12:02:59

标签: algorithm performance optimization parallel-processing parallelism-amdahl

我有一个代码
Floyd-Warshall algorithm表示NxN矩阵中的最短路径),有三个 for -loops,一个在另一个内,并具有相同的周期数。

在上一个 for 中,我通过三元操作= <bool> ? <val1> : <val2>进行了分配 - 基于比较,如果它是True。< / p>

我使用OpenMP将第二个 for #pragma omp parallel for并行化。

我无法计算代码的并行百分比和序列百分比,以成功应用Amdahl Law来恢复理论加速。

  for (k = 0; k < N; k++)
    #pragma omp parallel for  private(i,j) shared(x)  num_threads(4)
    for (i = 0; i < N; i++){
        for (j = 0; j < N; j++) {
            x[i][j] =  x[i][j] < (x[i][k] + x[k][j]) ? x[i][j] : (x[i][k] + x[k][j])  ;
        }
    }

我使用四个核心,所以我希望理论上的加速比为4。

X[i][j] 是矩阵,其中每个元素都充当连接节点ij的边的权重;如果他们没有连接,那就是宏INF(无限)。

2 个答案:

答案 0 :(得分:0)

<强> TL; DR:

很高兴大学在Amdahl's Law实例中花费更多时间来展示营销女孩和男孩如何轻易地创造出对多核和多核玩具的错误期望。

enter image description here

那就是说,让我们定义测试用例:

Floyd-Warshall Processing中的问题可以构成:

  1. 流程启动费用
  2. 数据结构内存分配+设置
  3. 数据值初始化
  4. Floyd-Warshall特定条件(归零对角线等)
  5. 章节计时工具
  6. 使用Floyd-Warshall O(N ^ 3)的过程,潜在的测试中的改进[IUT]
  7. Amdahl's Law声明任何流程整体&#34;改进&#34;的最终限制,因为第[6]节包含要评估的[IUT],而整体&#34;改进&#34;永远不会比~ 1 / ( ( 1 - IUT ) + ( IUT / N ) )好。

    有些读者可以测试并记录实验( 1 - IUT )部分的时间。

    如何计算代码部分[6]中可能并行化[IUT]的效果?

    首先,让我们关注原始发布的代码中发生的事情,在纯SEQ(串行)代码执行流程中:

    即使没有基于OpenMP的尝试将任务分配到更大的资源基础上,初始代码片段已经有了一些提升性能的空间:

     for        ( k = 0; k < N; k++ )
        for     ( i = 0; i < N; i++ ){
            for ( j = 0; j < N; j++ ){
                x[i][j] =  x[i][j] >  ( x[i][k] + x[k][j] )         // .TEST   <bool>
                                   ?  ( x[i][k] + x[k][j] )         // .ASSIGN <val1>
                                   :    x[i][j];                    // .ASSIGN <val2>
            }
        }
    

    如果这是纯粹SEQ独奏或尝试利用#pragma omp,则会在原始问题in both cases the Amdahl's Law will show ZERO or even "negative" improvement.

    中发布

    为什么呢?为什么不加速4?为什么甚至根本没有改进?

    因为代码被指示运行&#34;机械地&#34;重复所有资源,完全相同,完全相同的任务范围,完全4次,肩并肩,每一个除了其他,所以4倍的资源没有带来任何预期的积极影响,因为他们有他们一起花了相同的时间共同完成任务的所有部分4次,每次在其他部分上独立完成。潜力&#34;帮助&#34; (如果不是更糟,由于某些情况,在整个任务运行期间观察到资源争用时)。

    所以,让我们使用OpenMP优势来分割任务,并让每个资源处理算法范围的适当部分(感谢Floyd-Warshall算法,因为这个在这个方向上是非常宽容的并允许这样,因为它的处理方案,即使允许负权重,也是非干预的,所以不需要传播敌对障碍,同步,临界区来传播线程中的任何东西)

    那么,我们可以获得OpenMP的好处吗?哦,是的,很多:

    #include "omp.h"                        // .MUST SET a gcc directive // "-fopenmp"
         // --------------------------------------------------------[1] ref. above
    void main(){
               int i, j, k;
         const int N = 100;
               int x[100][100];
         // --------------------------------------------------------[2] ref. above
         // --------------------------------------------------------[3] ref. above
         // --------------------------------------------------------[4] ref. above
         for (          k = 0; k < N; k++ )
         {  
         // --------------------------------------------------------[5] ref. above
            //------------------------------------------------------[6]----- OMP
           // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
          //              PARALLEL is not precise, "just"-CONCURRENT is EXACT IN THE SECTION LEVEL BELOW
              #pragma omp parallel for  private(i,j) shared(x)  num_threads(4)
              for (     i =   0; i < N; i++ ){                                                                                  // .MUST  incl.actual k-th ROW, in case NEG weights are permitted
                  int  nTHREADs = omp_get_num_threads();                  // .GET "total" number of spawned threads
                  int       tID = omp_get_thread_num();                   // .GET "own"       tID# {0,1,..omp_get_num_threads()-1} .AVOID dumb repeating the same .JOB by all spawned threads
                  for ( j = tID; j < N; j += nTHREADs ){                  // .FOR WITH tID#-offset start + strided .INC STEP    // .MUST  incl.actual k-th COL, in case NEG weights are permitted
                      // - - - - - - - - - - - - - - - - - - - - - - - -  
                      // SINCE HERE:                                      // .JOB WAS SPLIT 2 tID#-ed, NON-OVERLAPPING tasks
                      // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // .N.B:  dumb "just"-CONCURRENT processing is O.K. here
                      // ................................................ //                             1-thread  .INC STEP   +1  a sure ZERO Amdahl-Law effect ( will bear an adverse penalty from use-less omp_get_*() calls )
                      // °.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°. //                             2-threads .INC STEP   +2     may have Amdahl-Law effect ~ 1 / ( ( 1 - OMP ) + ( OMP /   2 ) ) if enough free CPU-resources
                      // '-.'-.'-.'-.'-.'-.'-.'-.'-.'-.'-.'-.'-.'-.'-.'-. //                             3-threads .INC STEP   +3     may have Amdahl-Law effect ~ 1 / ( ( 1 - OMP ) + ( OMP /   3 ) ) if enough free CPU-resources
                      // ^'-.^'-.^'-.^'-.^'-.^'-.^'-.^'-.^'-.^'-.^'-.^'-. //                             4-threads .INC STEP   +4     may have Amdahl-Law effect ~ 1 / ( ( 1 - OMP ) + ( OMP /   4 ) ) if enough free CPU-resources
                      // o1234567o1234567o1234567o1234567o1234567o1234567 //                             8-threads .INC STEP   +8     may have Amdahl-Law effect ~ 1 / ( ( 1 - OMP ) + ( OMP /   8 ) ) if enough free CPU-resources
                      // o123456789ABCDEFo123456789ABCDEFo123456789ABCDEF //                            16-threads .INC STEP  +16     may have Amdahl-Law effect ~ 1 / ( ( 1 - OMP ) + ( OMP /  16 ) ) if enough free CPU-resources
                      // o123456789ABCDEFGHIJKLMNOPQRSTUVo123456789ABCDEF //                            32-threads .INC STEP  +32     may have Amdahl-Law effect ~ 1 / ( ( 1 - OMP ) + ( OMP /  32 ) ) if enough free CPU-resources
                      // o123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkl //                            64-threads .INC STEP  +64     may have Amdahl-Law effect ~ 1 / ( ( 1 - OMP ) + ( OMP /  64 ) ) if enough free CPU-resources
                      // o123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkl //                           128-threads .INC STEP +128     may have Amdahl-Law effect ~ 1 / ( ( 1 - OMP ) + ( OMP / 128 ) ) if enough free CPU-resources
    
                      int            aPair = x[i][k] + x[k][j];           // .MUST   .CALC ADD( x_ik, x_kj ) to TEST            // .MAY  smart re-use in case .GT. and ASSIGN will have to take a due place
                      if ( x[i][j] > aPair ) x[i][j] = aPair;             // .IFF    .UPD                                       // .AVOID dumb re-ASSIGN(s) of self.value(s) to self
                      // - - - - - - - - - - - - - - - - - - - - - - - -
                  }
              }
           // ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
         }// --------------------------------------------------------------- OMP
         return;
    }
    

    了解OpenMP beyond an Amdahl's Law predicted限制:

    通过一些有趣的实验,提出的方法为进一步探索开辟了一些潜力:

    • 将线程数设置为1(用作实验基线)
    • 将线程数设置为(nCPUcores / 2)
    • 将线程数设置为(nCPUcores - 1)
    • 将线程数设置为(nCPUcores)+运行磁盘碎片整理/压缩
    • 将线程数设置为(nCPUcores * 2)
    • 将线程数设置为(nCPUcores * 2)+仅在2个CPU核心上强制执行CPU亲和性
    • 将线程数设置为(nCPUcores * 20)
    • 设置行数/列数N~ {1.000 | 10.000 | 100.000 | 1.000.000}并检查效果

答案 1 :(得分:-1)

两个内部循环和最内层循环的主体在每个核心上串行执行。那是因为您标记了外部循环并行执行。

可是:

  • 我希望远远低于4的加速。总有一个通信开销
  • 最内层循环中的主体对所有核心使用相同的矩阵,并且还修改矩阵。因此,矩阵的变化必须支持所有其他核心。这可能会导致以下问题:

    1. CPU缓存可能没用,因为缓存的数组元素可能会被另一个可能具有不同缓存的核心更改。
    2. 在最坏的情况下,矩阵中的所有修改都取决于之前的更改(我没有根据您的情况进行检查)。在这种情况下,不可能并行执行=&gt;对于多个核心,根本没有加速。

您应该检查是否可以以不交叉部分矩阵的方式更改算法。如果核心处理单独的不相交数据,您将获得最佳加速。

由于几乎没有这么做,你应该明确地描述它的代码和变体。