OpenMP第一核比第二个内核慢得多

OpenMP first kernel much slower than the second kernel

本文关键字:内核 第二个 OpenMP      更新时间:2023-10-16

我有一个巨大的98306乘98306 2D阵列初始化。我创建了一个内核函数,该函数计算以下阈值以下的元素总数。

#pragma omp parallel for reduction(+:num_below_threshold)
for(row)
    for(col)
        index = get_corresponding_index(row, col);
        if (array[index] < threshold)
            num_below_threshold++;

出于基准目的,我测量了当线程数设置为1时的内核执行时间。在同一数组上使用一个线程执行的内核的下一个呼叫只需大约3秒钟。我认为这可能是与缓存有关的问题,但似乎没有关系。引起这一点的可能原因是什么?

此数组的初始化为:

float *array = malloc(sizeof(float) * 98306 * 98306);
for (int i = 0; i < 98306 * 98306; i++) {
    array[i] = rand() % 10;
}

将同一内核应用于此数组两次,第二个执行时间比第一个内核快得多。我虽然在Linux上分配了懒惰,但由于初始化功能,这应该不是问题。任何解释都会有所帮助。谢谢!

由于您没有提供任何最小,完整和可验证的例子,所以我必须在这里做出一些疯狂的猜测,但是我很有信心我有这个问题的要素。

首先,您必须注意,98,306 x 98,306是9,664,069,636,它比签名的32位整数可以存储的最大值大(2,147,483,647)。因此,您的for初始化循环的上限在溢出后可能会变成1,074,135,044(尽管在我的机器上,尽管严格来说,这是不确定的行为,这一切可能发生),这可能比您预期的要小9倍。p>现在,在初始化循环之后,实际上只有11%的内存实际上是由操作系统分配和触摸的。但是,您的第一个减少循环在浏览阵列的各个元素方面做得很好,并且由于大约89%,因此在拳头时间内,操作系统在那里进行实际内存分配,然后需要一些重要的时间。

现在,对于您的第二次减少循环,所有内存都已正确分配和触摸,这使其更快。

所以这就是我相信发生的事情。也就是说,许多其他参数可以在这里发挥作用,例如:

  • 交换:您尝试分配的数组代表大约36GB的内存。如果您的计算机没有太多的内存,那么您的代码可能会交换,这可能会使您可以提出的任何性能衡量范围都大致混乱
  • numa效果:如果您的计算机具有多个NUMA节点,则在无法正确管理的情况下,线程固定和内存亲和力会对循环出现之间的性能产生很大的影响
  • 编译器优化:您没有提及所需的哪个编译器以及所请求的优化级别。根据这一点,您会惊讶于代码的缩短。例如,编译器可以完全删除第二个循环,因为它与第一个循环一样,并变得无用,因为结果将是相同的……以及许多其他有趣且出乎意料的事物,使您的基准测试毫无意义
相关文章: