Language agnostic 使用OpenMP实现多级别并行-可能吗?聪明?实用的? 我目前正在研究一个C++稀疏矩阵/数学/迭代求解器库,用于管理我的仿真工具。我更愿意使用现有的软件包,但是,经过广泛的调查,没有发现适合我们的模拟器的软件包(我们查看了flens、it++、PetSC、eigen和其他几个软件包)。好消息是我的解算器和稀疏矩阵结构现在非常高效和健壮。坏消息是,我现在正在研究使用OpenMP进行并行化,学习曲线有点陡峭

Language agnostic 使用OpenMP实现多级别并行-可能吗?聪明?实用的? 我目前正在研究一个C++稀疏矩阵/数学/迭代求解器库,用于管理我的仿真工具。我更愿意使用现有的软件包,但是,经过广泛的调查,没有发现适合我们的模拟器的软件包(我们查看了flens、it++、PetSC、eigen和其他几个软件包)。好消息是我的解算器和稀疏矩阵结构现在非常高效和健壮。坏消息是,我现在正在研究使用OpenMP进行并行化,学习曲线有点陡峭,language-agnostic,parallel-processing,openmp,linear-algebra,hpc,Language Agnostic,Parallel Processing,Openmp,Linear Algebra,Hpc,我们求解的域可以分解为子域,这些子域以块对角格式聚集在一起。因此,我们的存储方案最终看起来像一个较小的方阵(块[])阵列,每个方阵的格式适合子域(例如,压缩行存储:CRS、压缩对角存储:CDS、稠密等),以及一个说明子域之间连接的背景矩阵(当前使用CRS) 大多数(全部?)迭代解算器中的“热点”是矩阵向量乘法运算,我的库也是如此。因此,我一直致力于优化我的MxV例程。对于块对角结构,M*x=b的伪码如下: b=background_matrix*x start_index = 1; end_in

我们求解的域可以分解为子域,这些子域以块对角格式聚集在一起。因此,我们的存储方案最终看起来像一个较小的方阵(块[])阵列,每个方阵的格式适合子域(例如,压缩行存储:CRS、压缩对角存储:CDS、稠密等),以及一个说明子域之间连接的背景矩阵(当前使用CRS)

大多数(全部?)迭代解算器中的“热点”是矩阵向量乘法运算,我的库也是如此。因此,我一直致力于优化我的MxV例程。对于块对角结构,M*x=b的伪码如下:

b=background_matrix*x
start_index = 1;
end_index = 0;
for(i=1:number of blocks) {
    end_index=start_index+blocks[i].numRows();
    b.range(start_index, end_index) += blocks[i] * x.range(start_index, end_index);
    start_index = end_index+1;
}
其中,background_矩阵是背景(CRS)矩阵,blocks是子域矩阵的数组,range返回从起始索引到结束索引的向量部分

显然,循环可以(并且已经)并行化,因为操作独立于循环的其他迭代(范围不重叠)。因为我们在一个典型的系统中有10-15个块,4个以上的线程实际上会产生显著的差异

在中,并行化被认为是一个不错的选择的另一个地方是每个子域存储方案的MxV操作(在上述代码的第1行和第6行中调用)。在并行化CRS、CD和密集矩阵MxV操作方面有很多。通常情况下,2个线程会带来很好的提升,随着更多线程的添加,回报会大大减少


我设想了一个方案,在上面代码的块循环中使用4个线程,每个线程将使用2个线程进行子域求解。但是,我不确定如何使用OpenMP管理线程池—是否可以限制OpenMP for循环中的线程数?这种多级并行在实践中有意义吗?如果您对我在这里提出的建议有任何其他想法,我们将不胜感激(感谢您一直阅读到最后!)

为什么不请专家们访问OpenMP.org

注册并登录到:

请注意,我描述的所有内容都依赖于实现

是否可以限制openmp for循环中的线程数?

对。有不同的方法可以做到这一点。Set
omp\u Set\u嵌套(1)#pragma omp parallel for num_线程(4)
或类似的代码,在内循环中使用
#pragma omp parallel for num_线程(2)
指令。这将为您提供8个线程(取决于实现,如果您的内核少于8个,您可能还必须设置
OMP\u THREAD\u LIMIT

或者,您可以手动展开循环,例如使用

#pragma omp parallel sections {
     #pragma omp section 
     do your stuff for the first part, nest parallel region again
     #pragma omp section 
     and so on for the other parts
}
在OpenMP 3.0中,使用
#pragma omp task
有时可以更高效地完成同样的任务

或者启动8个线程,获取并行部分中的当前线程编号,并根据线程编号手动调度

最后,如果您有一个完全嵌套的循环(一个循环是完全嵌套的,如果实际的赋值只发生在最里面的循环中),那么您可以将所有内容重写为一个循环。基本上把两个迭代器
i
j
组合成一个大迭代器
(i,j)
。请注意,这可能会减少局部性,从而降低性能

这种多级并行在实践中有意义吗?

这要看情况而定,你必须了解自己。通常,多级并行性使问题更具可伸缩性。然而,日程安排可能更为复杂。这可能很有趣

关于手动设置线程数:设置线程数的主要优点是,您可以在调度时使用有关问题的特定知识。 因此,您可以减少开销并获得正在执行的代码的更高的局部性,从而获得更多的缓存命中和更少的主内存I/O

手动设置嵌套并行中的线程数的主要缺点是,最内层循环中的线程可能会在隐式屏障处空闲等待,而额外的工作可以完成()。此外,粗粒度并行性不能很好地扩展。因此,如果您的外部循环在循环中有非常不同的运行时,那么您希望更灵活地调度,而不是简单地拆分为4个线程

还有其他想法吗

您是否考虑过使用SIMD进行MxV。根据体系结构的不同,这可以提供2-4的加速。我很快为你搜索了这个


对于MxV和相关技术,可以增加数据局部性并减少其他问题,例如错误共享。第11章(您可以预览)可能会为您提供一些关于如何重新构造数据访问的额外想法。

您最终使用了哪种解算器?@Jacob-我正在解决的系统有几种不同类型的子域,使用CRS上的jacobi预调GMRES、CD上的ICC预调CG solve,可以最有效地解决这些子域,或直接稠密解。为了最大限度地利用每个子域,我在全局系统上使用了GMRES解算,使用了一步非重叠加法Schwarz预条件器,其中预编辑器中的局部解算是适当的解算算法