我正在开发一个最初为多核处理器系统开发的遗留应用程序。为了利用多核处理,已经使用了OpenMP和PPL。现在,一个新的要求是在多个NUMA节点的系统上运行该软件。目标操作系统是Windows7 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]);
}据我所知,这种算法的性能下降是由来自第二个Node的非本地内存访问造成的。因此,问题在于如何使应用程序更好地执行。
对非本地内存的只读访问是否以某种方式透明地加速(例如,通过操作系统将数据从一个节点的本地内存复制到另一个节点的本地内存)?我是否必须拆分问题大小并将输入数据复制到相应的NUMA节点,然后处理它,然后再将所有NUMA节点的数据合并以提高性能?
如果是这样的话,那么在分配内存时,是否存在std容器的替代方案,因为这些容器在分配内存时不了解NUMA?
发布于 2018-03-05 08:15:20
当您分配动态内存(如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内存也可能要快得多。整个问题可以隐藏在这个函数中,但是我们不知道它能做什么。
https://stackoverflow.com/questions/49105427
复制相似问题