天天看点

MIC编程优化(1)——并行度优化

在计算机体系结构中,并行度是指指令并行执行的最大条数。在设计并行程序时,我们可以简单地把并行度认为是在多核/众核处理器上能同时执行的线程数/进程数。对于同一个程序,并行度设计方法的不同将会严重影响到程序的性能。MIC上的并行度优化主要涉及并行线程/进程的数目、并行层级、并行粒度等方面。  

1 并行度

MIC卡包含众多的物理核,同时每个核上可以开启4个线程,因此,程序员只有设计足够多的线程/进程才可以把所有的核利用起来。例如一块60个核的MIC卡上,我们最多可以开启240个线程,最佳线程数一般是每个核设置3个或4个线程,图1展示的是某一实际高性能应用程序在60个核的MIC卡上设置不同线程数的性能扩展性结果图,从该图可以看出,只有让MIC卡上的所有核都充分利用起来才能发挥MIC的最大性能。当然,也不是在MIC卡上设置的线程数越多越好,线程数太多的话,线程开销比较大,我们只需要设置的线程数可以保证程序并发度和MIC核的高利用率即可。

MIC编程优化(1)——并行度优化

图1 某一高性能应用程序在MIC上的性能扩展性

2并行粒度

并行程序是否选择了合适的层级上实现并行,是性能优化中需要关心的重要问题。根据并行程序尽可能使用粗粒度的并行原则,尽可能在最上层并行化代码。在外层上并行除了带来易编程的好处之外,还可以带来好的性能:增加粒度,减少了线程调度和销毁的次数,也就是减少了线程本身的开销所占的比例,尤其对于MIC平台要开启上百个线程,减少线程的开启对性能影响更为重要;同时,隐藏了底层的线程交互,减少了不必要的同步带来的损耗。

下面通过简单的例子说明并行层级,例如程序中有两层循环,并且每层循环都没有数据依赖性,即两层循环都可以并行,根据并行程序尽可能使用粗粒度的并行原则我们可以采用在i层循环并行的方式。

1.          #pragma omp parallel for num_threads(THREAD_NUM)

2.          for (i=0; i<M; i++)

3.          {

4.              for (j=0; j<N; j++)

5.              {

6.                  …

7.              }

8.          }

当然,并不是所有的应用程序都是在外层循环并行效果最佳,外层循环的并行可能会导致线程之间访问的数据跨度比较大,可能会引起Cache miss,这种情况下可能采取内层循环的并行效果更佳,同时为了减少线程的开销,我们可以在外层for之前开启多线程,在内层for进行任务分发,如上面的代码采用下面的并行方式。

1           #pragma omp parallel num_threads(THREAD_NUM)

2           for (i=0; i<M; i++)

3           {

4           #pragma omp for

5                    for (j=0; j<N; j++)

6                    {

7                              …

8                }

9           }

在实际的应用程序中也可能出现某一层循环无法达到MIC的并行度要求,针对这种情况,我们可以采取多层循环合并的方式。例如上面的代码中M=20,N=30,无论我们并行哪层for都无法达到MIC的并行度要求,我们可以合并两层for,合并之后的循环次数为600次,显然可以满足MIC平台上的要求。当然,我们也可以采用嵌套并行的方式满足MIC的并行度要求。

合并循环:

1           #pragma omp parallel for num_threads(THREAD_NUM)

2           for (k=0; k<M*N; k++)

3           {

4               i = k/M;

5               j = k%M;

6               …

7           }

嵌套并行:

1           omp_set_nested(true); //允许嵌套并行

2           #pragma omp parallel for num_threads(THREAD_NUM1)

3           for (i=0; i<M; i++)

4           {

5           #pragma omp parallel for num_threads(THREAD_NUM2)

6               for (j=0; j<N; j++)

7               {

8                              …

9               }

10        }

继续阅读