C- NUMA处理器上的OpenMP内存分配



我当前正在尝试加快Maestro处理器上的OpenMP的简单矩阵减法基准,该基准在Maestro处理器上具有NUMA架构,并基于Tileera Tile64处理器。Maestro板有49个处理器以7x7配置的二维阵列排列。每个核心都有自己的L1和L2缓存。董事会的布局可以在这里看到:https://i.stack.imgur.com/rg0fc.png

我是编写" Numa Aware"应用程序的想法的新手,但是我阅读的主要共识是Data Locality是最大化性能的重要组成部分。当核心之间的代码并行化时,我应该将被本地使用的数据保留为线程进行处理。

对于此矩阵减法基准(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];
       }
}

数组的初始值是任意的,我只有op_get_thread_num(),因此我从每个线程中获得C_PRIV中的不同值。目前,我正在尝试使用用户动态网络,该网络提供了可以在CPU之间进行硬件路由包装包的硬件,以便将所有单个线程结果累积到最终结果的数组中。

我已经实现了加速,并使用op_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:对于时间表,它将数据分配给每个线程,以便每个线程都在其本地数据上工作。

我只是觉得像矩阵减法一样简单,在使用OpenMP时不难提高性能。我不确定该如何弄清楚瓶颈是什么以及如何减轻瓶颈。

在快速搜索和扫描tile64数据表中,它看起来并不像通过oprofile,vtune或xperf这样的工具在x86上使用x86上使用的架构计数器。没有这些,您将不得不设计自己的一些实验,以迭代地缩小代码的哪一部分,以及为什么 - 在没有微体系图谱的情况下以及工具以及指示您的代码如何行使硬件的工具,有点逆向工程任务。

关于从哪里开始的一些想法:

  1. 进行一些缩放实验。曲线中是否有膝盖越过某个问题大小或线程数量对整体性能有很大影响?该数字是否暗示与内存层次结构中某个级别的大小或处理器网格的尺寸或类似的?
  2. 通过程序的几点记录执行时间。例如,在高级别上知道在mallocs上花费了多少时间与第一个循环相对于第二个。
  3. 可能很有用。
  4. "我已经达到了这种方式的加速,并使用op_proc_bind = true固定线程,但我担心将单个结果累积到最终数组中可能会导致开销,从而否定速度。" - 这种担心在经验上也可以测试,尤其是如果您处理足够大的问题大小,以至于(2)中的计时器准确性不是隔离收集步骤所花费的时间与完全平行的部分的问题。<<<<<<<<<<<<<<<<<<<</li>
  5. 尝试其他操作 - 例如,加法或元素划分而不是减法,看看是否会更改结果。在许多体系结构上,不同的算术操作具有不同的延迟和吞吐量。如果您抬头看并发现Tile64就是这种情况,那么进行这样的更改并启动第二个示例的运行时可能会告诉您一些有用的事情,以了解串行运行的时间实际上与数据有关局部问题与启动时间或与OpenMP运行时相关的其他间接费用,在整体结果中可能要做的与小问题大小的关系相比,与并行实现的正确并行部分实际运行较慢。
  6. 您可以检查生成的组件。在您发布的示例中,编译器基本上可以做相同的事情的假设似乎是合理的,但不一定像您在查看奇怪的性能时那样坚强。也许关于代码大小或布局在有/没有OpenMP的情况下发生变化,或者从一个并行方法转移到另一种并行方法时,例如使用指令缓存,可用性保留站或Rob条目(如果TELE64有这些东西)...?谁知道,直到你看。

相关内容

  • 没有找到相关文章

最新更新