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

时间:2019-03-20 23:11:10

标签: c++ c multithreading parallel-processing openmp

我通过98306 2D数组初始化了一个庞大的98306。我创建了一个内核函数,该函数计算低于某个阈值的元素总数。

#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时内核执行的执行时间。我注意到内核第一次执行大约需要11秒。下一次对在一个线程上在同一阵列上执行的内核的调用仅花费了大约3秒钟。我认为这可能是与缓存有关的问题,但似乎并不相关。造成这种情况的可能原因是什么?

此数组初始化为:

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

同一内核两次应用于此阵列,第二次执行时间比第一个内核快得多。尽管我在Linux上分配了延迟,但是由于初始化功能,这应该不会成为问题。任何解释都会有所帮助。谢谢!

1 个答案:

答案 0 :(得分:4)

由于您没有提供任何Minimal, Complete and Verifiable Example,因此我不得不在这里做出一些疯狂的猜测,但是我非常有信心要解决这个问题。

首先,您必须注意到98,306 x 98,306为9,664,069,636,这远大于一个有符号的32位整数可以存储的最大值(即2,147,483,647)。因此,import autopep8 autopep8.fixcode('your_code') 初始化循环的上限在溢出后可能变为1,074,135,044(就像在我的机器上一样,尽管严格来说,这是未定义的行为,否则可能会发生任何事情),大约比您小9倍。预期的。

因此,现在,在初始化循环之后,操作系统认为实际上只分配了11%的分配内存。但是,您的第一个精简循环在遍历数组的各个元素方面做得很好,由于大约有89%的时间是第一次,因此OS会在那里实际进行内存分配,这需要花费大量时间。时间量。

现在,对于您的第二个缩减循环,所有内存都已正确分配和使用,这使其速度更快。

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

  • 交换:您尝试分配的阵列代表约36GB的内存。如果您的计算机没有足够的可用内存,则您的代码可能会交换,这可能会给您可以提供的任何性能度量带来很大的麻烦
  • NUMA效果:如果您的计算机具有多个NUMA节点,则在未正确管理的情况下,线程固定和内存关联会严重影响循环发生之间的性能
  • 编译器优化:您没有提及您使用的编译器以及所请求的优化级别。以此为依据,您会对代码缩短的时间感到惊讶。例如,编译器可以完全删除第二个循环,因为它执行的操作与第一个循环相同,并且变得无用,因为结果将是相同的……还有许多其他有趣且意外的事情,这些事情使基准测试毫无意义。