NUMA处理器上的OpenMP内存分配

时间:2017-02-22 03:04:00

标签: c multithreading memory openmp numa

我目前正在尝试使用Maestro处理器上的OpenMP加速简单的矩阵减法基准测试,该处理器具有NUMA架构并基于Tilera Tile64处理器。 Maestro板有49个处理器,以7x7配置排列成二维阵列。每个核心都有自己的L1和L2缓存。在这里可以看到电路板的布局:http://i.imgur.com/naCWTuK.png

我对编写'NUMA-aware'应用程序的想法不熟悉,但我所读到的主要共识是数据局部性是最大化性能的重要部分。在核心之间并行化代码时,我应该尽可能地将数据保持在进行处理的线程本地。

对于这个矩阵减法基准(C [i] = A [i] - B [i]),我认为为每个线程分配它自己的私有A,B和C数组是个好主意是总工作量除以线程数。因此,例如,如果阵列的总大小是6000 * 6000并且我试图在20个线程中并行化它,我将分配大小为(6000 * 6000)/ 20的私有数组。每个线程都会在自己的私有数组上执行此减法,然后我会将结果收集回总大小为6000 * 6000的最终数组中。例如(没有将每个线程的结果收集到最终数组中):

int threads = 20;
int size = 6000;
uint8_t *C_final = malloc(sizeof(uint8_t)*(size*size));
#pragma omp parallel num_threads(threads) private(j)
{
     uint8_t *A_priv = malloc(sizeof(uint8_t)*((size*size)/threads));
     uint8_t *B_priv = malloc(sizeof(uint8_t)*((size*size)/threads));
     uint8_t *C_priv = malloc(sizeof(uint8_t)*((size*size)/threads));

     for(j=0; j<((size*size)/threads); j++)
       {
            A_priv[j]=100;
            B_priv[j]=omp_get_thread_num();
            C_priv[j]=0;
       }

     for(j=0; j<((size*size)/threads); j++)
       {
           C_priv[j] = A_priv[j]-B_priv[j];
       }
}

数组的初始值是任意的,我只有omp_get_thread_num(),所以我在每个线程的C_priv中得到不同的值。我目前正在尝试用户动态网络,该板提供了硬件来在CPU之间路由数据包,以便将所有单独的线程结果累积到最终生成的数组中。

我已经通过这种方式实现了加速,同时使用OMP_PROC_BIND = true固定线程,但我担心将单个结果累积到最终数组可能会导致开销,从而抵消加速。

这是解决此类问题的正确方法吗?对于像这样使用OpenMP的问题,我应该考虑采用什么类型的技术来加速NUMA架构?

编辑:

为了澄清,这是我最初尝试过的,而且我注意到执行时间比我刚刚连续运行代码的时间要慢:

     int threads = 20;
     int size = 6000;
     uint8_t *A_priv = malloc(sizeof(uint8_t)*(size*size));
     uint8_t *B_priv = malloc(sizeof(uint8_t)*(size*size));
     uint8_t *C_priv = malloc(sizeof(uint8_t)*(size*size));

     int i;
     for(i=0; i<(size*size); i++)
     {
       A[i] = 10;
       B[i] = 5;
       C[i] = 0;
     }

     #pragma omp parallel for num_threads(threads)
     for(i=0; i<(size*size); i++)
     {
       C[i] = A[i] - B[i];
     }

在看到我使用OpenMP后执行时间变慢后,我试着调查为什么会这样。似乎数据位置就是问题所在。这个假设是基于我读过的有关NUMA架构的内容。

我正在努力弄清楚如何缓解导致其减速的瓶颈。我找到了类似这样的问题的一些帮助:OpenMP: for schedule其中涉及为每个线程分配数据,以便每个线程处理其本地数据。

在使用OpenMP时,我觉得像矩阵减法这样简单的事情并不难以提高性能。我不知道如何确定瓶颈究竟是什么以及如何减轻瓶颈。

1 个答案:

答案 0 :(得分:0)

在快速搜索和扫描TILE64数据表时,该体系结构看起来并不像通过oprofile,VTune或xperf之类的工具公开性能计数器那样。没有这些,您将不得不设计一些自己的实验来迭代地缩小代码的哪些部分以及原因-在没有微体系结构文档和工具的情况下,这些工具可以指示您的代码如何行使硬件,逆向工程任务。

从何处开始的一些想法:

  1. 进行一些缩放实验。在曲线上是否存在拐点,在某个特定的问题大小或线程数量上遍历会对整体性能产生重大影响?这个数字是否暗示着与内存层次结构中某个级别的大小,处理器网格的大小或类似的东西之间存在明显的关系?
  2. 通过程序记录执行时间。例如,从高层了解在malloc上相对于第一个循环与第二个循环所花费的时间可能是有用的。
  3. “通过用OMP_PROC_BIND = true固定线程,我已经实现了这种方式的加速,但是我担心将单个结果累加到最终数组中会导致开销,而这会抵消加速。” -这种担心也可以凭经验进行测试,特别是如果您正在处理足够大的问题,以至于(2)中的计时器精度对于隔离收集步骤与完全可并行化的部分所花费的时间不是问题。 / li>
  4. 尝试其他操作-用加法或按元素除法而不是减法,看看是否会改变结果。在许多体系结构上,不同的算术运算具有不同的延迟和吞吐量。如果您查找并发现TILE64就是这种情况,那么进行这样的更改并测试第二个示例的运行时,可能会告诉您一些有用的信息,即串行运行它实际上花费了多少时间与数据有关本地性问题与启动时间或其他与OpenMP运行时相关的开销,与总体问题相比,与小问题大小的关系可能要多得多,而不是并行实现的适当并行部分实际运行速度较慢。
  5. 您可以检查生成的程序集。假设编译器在您发布的示例中将执行基本相同的操作的假设似乎是合理的,但并不一定能像您看到奇数性能时那样坚如磐石。也许有一些关于代码大小或布局的信息,在有/没有OpenMP的情况下,或者从一种并行方法转移到另一种并行方法时,例如使用指令高速缓存,预留站或ROB条目的可用性(如果TILE64有这些东西)...谁知道,直到你看。