C ++ NUMA优化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C ++ NUMA优化相关的知识,希望对你有一定的参考价值。

我正在研究最初为多核处理器系统开发的遗留应用程序。为了利用多核处理,已经使用了OpenMP和PPL。现在,新要求是在具有多个NUMA节点的系统上运行该软件。目标操作系统是Windows 7 x64。

我已经进行了几次测量,并注意到在将应用程序分配给单个NUMA节点时执行时间最佳,因此浪费了完整的处理器。应用程序的许多部分执行数据并行算法,例如,并行处理向量的每个元素,并将结果写入另一个向量,如下例所示

std::vector<int> data;
std::vector<int> res;

// init data and res

#pragma omp parallel for
for (int i = 0; i < (int) data.size(); ++i)
{  
  res[i] = doExtremeComplexStuff(data[i]);
}

据我所知,此类算法的性能下降是由第二个NUMA节点的非本地内存访问引起的。所以问题是如何使应用程序更好地运行。

是否以某种方式透明地加速了对非本地存储器的只读访问(例如,OS将数据从一个节点的本地存储器复制到另一个节点的本地存储器)?我是否必须拆分问题大小并将输入数据复制到相应的NUMA节点,处理它,然后再次组合所有NUMA节点的数据以提高性能?

如果是这种情况,是否有std容器的替代品,因为在分配内存时这些不是NUMA感知的?

答案

当您分配动态内存(例如std::vector)时,您可以有效地从虚拟内存空间获取一些范围的页面。当程序首次访问特定页面时,会触发页面错误并请求物理内存中的某些页面。通常,此页面位于生成页面错误的核心的本地物理内存中,这称为第一个触摸策略。

在您的代码中,如果std::vector的缓冲区的页面首先被单个(例如,主)线程触及,则可能发生这些向量的所有元素最终都在单个NUMA节点的本地存储器中。然后,如果将程序拆分为在所有NUMA节点上运行的线程,则某些线程在处理这些向量时会访问远程内存。

因此,解决方案是分配“原始内存”,然后首先用所有线程“触摸”它,然后在处理阶段由这些线程访问它们。不幸的是,使用std::vector并不容易实现,至少对于标准分配器。你能切换到普通的动态阵列吗?我会首先尝试这一点,看看他们的初始化是否有助于:

int* data = new int[N];
int* res = new int[N];

// initialization with respect to first touch policy
#pragma omp parallel for schedule(static)
for (int i = 0; i < N; i++) {
   data[i] = ...;
   res[i] = ...;
}

#pragma omp parallel for schedule(static)
for (int i = 0; i < N; i++)
   res[i] = doExtremeComplexStuff(data[i]);

使用static调度,元素到线程的映射应该在两个循环中完全相同。


但是,在访问这两个向量时,我不相信您的问题是由NUMA效应引起的。当你调用函数doExtremeComplexStuff时,似乎这个函数对运行时来说非常昂贵。如果这是真的,与函数调用相比,即使访问远程NUMA内存也可能是可以忽略不计的。整个问题可以隐藏在这个函数中,但我们不知道它的作用。

以上是关于C ++ NUMA优化的主要内容,如果未能解决你的问题,请参考以下文章

NUMA的取舍与优化设置

⭐NUMA架构进行优化,数据库效率显著提升,那么什么是NUMA?微动画带你走进全新NUMA⭐

Linux上MySQL优化提升性能 哪些可以优化的关闭NUMA特性

mysql 内存优化之关闭numa

修改numa和io调度优化mysql性能

Linux 操作系统原理 — NUMA 架构中的多线程调度开销与性能优化