在上一篇关于分布式有限元计算的文章中,我们定义分布式计算为计算单元的内存地址不能被其他计算单元直接读写,只有通过网线连接的计算方式。而非分布式计算中计算单元的内存地址可以互相访问,或者虽然它们不能直接内存互访,但是可以通过机内数据总线,而不是通过网络通信相互访问的计算方式。由于这些并行工作的计算单元都组装在一个计算节点(computer node, 详细请参见下面的链接)内,我们称之为计算节点内的并行计算。
最近在各种会议上遇到NEC的员工介绍他们的新型并行计算机SX-Aurora TSUBASA,并有销售人员访问各软件公司,言称可以提供免费的硬,软件资源给软件公司用来移值其软件到这种新型的计算机上。
SX-Aurora TSUBASA的核心是一种新型的矢量计算模块,很漂亮的小型芯片,跟图形卡一样,可以直接插入到计算机主板上,然后配合相应的软件就可使用。
我对使用这种计算机兴趣不大,毕竟我不可能花钱买一台这样最便宜也要数百万日元的计算机。但是,这样的新型计算机的出现反映了这样一个事实: 由于计算硬件主频的限度,提高计算速度的主战场已经转移到并行计算上,由此出现了各种各样的硬件设计和相应的软件实现,如OpenMP, OpenACC, RAJA, SyCL,C AMP等等。这一现实,对软件开发专业人士来说也许是机会和挑战。但是对于科学计算软件开发者来说则更多的是困惑: 因为他们的关心的重点不在于此,他们的主要精力还是在物理理解,算法开发、或者工业应用上。而且这里有个根本的问题需要解决: 难道对每一种硬件我们都要开发出相应的软件吗?不过,换个角度而言现在的这种状况也未尝不是一种机遇: Abaqus,ANSYS这样的占有压倒性市场地位的软件开发的时间够长(因此他们的信赖性够好),但是他们对这样的新型计算机的对应性不会太好,或者根本不能对应。新开发的软件在这一点上应该很容易超越他们。
必须开发一种方法用来隔离科学计算软件开发对硬件的依存。 其基本的想法就是导入一个中间层(Middleware)隔开硬件与应用软件。硬件依存关系在这个中间层中处理,而应用软件则无需过多考虑硬件实现问题。如此做法,应用层的开发人员也许会失去对硬件层的细节控制,但是可以降低编程门槛,节省软件开发时间,得到通用可移值的软件。专业的工作让专业人士来做。一般情况下,在对硬件控制这个领域,应用层的开发人员不可能比中间层的开发人员做得更好。
Kokkos就是为了实现上述功能,现在还在活跃地进行开发和维护的软件实装。
此外,具有相似功能的还有
https://icl.utk.edu/magma/software/index.htmlicl.utk.edu/magma/software/index.html
还有(好像已经停止维护了?)
Linear Algebra Library using CUDA, OpenCL, and OpenMPviennacl.sourceforge.net/
让我们看看Kokkos目前对应的硬,软件类型(2)
另外,Kokkos的开发人员中有数人是ISO C Committee的成员,Kokkos的实装方法很有可能被导入ISO C 标准(2)。
Kokkos是一个相对庞大的系统,它大致长这样(2)。需要注意的是,Kokkos只是负责计算节点内的并行计算。计算节点间的分布式并行计算需要由其他软件来实现,具体可以参照这篇文章的图1和这篇文章中的详细说明。
在上图所示的异性构架中,数据存储位置(memory space)和数据计算位置(execution space)都可以有多种选择。Kokkos提供了相应功能来对应这一变化。
其数据定义方法如下
Kokkos::View<double***,MemorySpace> data;
定义了double类型数据的三维动态数组。其存储位置由模板变量MemorySpace指定。这个模板变量可以是HostSpace, CudaSpace, CudaUVMSpace, CudaHostPinnedSpace等。
Kokkos下的并行计算命令有parallel_for和parralel_reduce.
Kokkos::View<double***> somedata;
parallel_for("Label",RangePolicy< ExcutionSpace > (0, numofIteration), [=]( const int64_t i )
{ use somedate do some calculation here };
其计算执行位置由模板变量ExcutionSpace指定。可以是Serial, Threads, OpenMP, Cuda, HIP等。如此Kokkos通过良好的接口设计,把内存对齐、下标映射、异构设备内存和主内存之间的通信同步等等封装起来。通过Kokkos界面,用户可以通过统一的接口,按需要选择硬件设备而无须处理其具体执行的细节。
下面的例子为在ne个有限元单元中对标量场s积分。也就是计算 ∑e=1ne∑i=1ngNiesi . 我们需要先定义一个在ne个单元上的场量field_
field_ = Kokkos::View<ScalarT, Cell, BASIS>(fieldName, basis.functional)
然后定义一个用于计算单元上field_ 量的KOKKOS_INLINE_FUNCTION函数
KOKKOS_INLINE_FUNCTIONvoid MyClass::operator( const std::size_t& cell){
for (int qp=0; qp < numQP; qp)
{
for (int basis=0; basis < numBases; basis)
field_(cell, basis) = basis_(cell, basis, qp) * scalar_(cell, qp);
}}
然后在ncell单元上做积分运算即可。
parallel_for(RangePolicy<ExcutionSpace>(0, ne), *this);
这样的程序只需要适当的调整模板参量就可对应各种不同硬件上的并行计算。