c++26新功能—多维数组视图
一、std::span和std::mdspan
在C++20和C++23分别推出了std::span和std::mdspan,大家可以理解为一种视图或者Go语言中的切片,当然这种理解不严谨,但这样做容易让大家接受,算是一种折衷吧。做为一种多维数组视图,在C++26中对std::mdspan进行了更强大的扩展。其实细想一下就明白了,无论是并行计算还是高性能计算(HPC),其实都会用到各种矩阵、向量等等的操作,而多维的数组视图天然在这方面就有优势。
二、std::mdspan的说明
在C++26中对std::mdspan增加了几个关键的特性:
1、维度定义
支持定义固定维度和动态维度以及混合维度的维度定义,这在CUDA等并行编程中非常有用
2、布局策略
为了优化和提高内存的效率,支持了:layout_right (默认),layout_left,layout_stride,layout_right_padded等四类策略。其中layout_right支持行优先的内存顺序而layout_left支持列优先的内在顺序。前者更适合于图像和数学计算等,后者更适合于BLAS( Basic Linear Algebra Subprograms,基本线性代数子程序)和LAPACK(Linear Algebra PACKage,线性代数包)。layout_right_padded,支持带对齐的行优先内存顺序,更适合于SIMD优化。而layout_stride则为自定义的情况,即在一些特定的场合下,可以自行定义并应用。
3、访问器
在C++26中提供了std::aligned_accessor,用来更方便的访问其中的数据
4、子视图
在C++26中增加对std::submdspan子视图的操作管理,这个更像切片,子视图的优势在于可以对数据进行广泛的重用,而不需不断的进行拷贝等操作。
三、std::mdspan的应用
在分析std::mdspan的基础上,看一下如何对其进行应用:
1、基本应用
//维度定义
// 固定维度 (2x3)
using M1 = mdspan<float, extents<2, 3>>;
// 动态维度 (NxM)
using dM1 = mdspan<int, dextents< 4>>;//等价于mdspan<int, extents<dynamic_extent, dynamic_extent,dynamic_extent,dynamic_extent>>;
// 混合维度 (4xM)
using mM1 = mdspan<double, extents<4, dynamic_extent>>;
//访问器
void compute_with_aligned(std::mdspan<float, std::dims<2>, std::layout_left> matrix)
{const std::size_t byte_alignment = 4 * alignof(float);std::mdspan aligned_matrix{matrix.data_handle(), matrix.mapping(),std::aligned_accessor<float, byte_alignment>{}};// ... use aligned_matrix ...
}
//64字节对齐 (AVX512)
alignas(64) float data[1024];
mdspan<const float, extents<16, 64>> aligned_view(data);
//内存布局
// BLAS兼容的列优先视图
mdspan<double, dextents<2>, layout_left> bv(A.data(), m, n);
2、子视图
下面的例子是“给定一个表示矩形棱柱中规则间隔点的三维网格的rank-3 mdspan grid3d,函数zero_surface将三维形状表面上的所有元素设置为零。它通过重用一个秩为2的mdspan函数zero_2d来实现这一点”:
template<class T, class E, class L, class A>
void zero_2d(mdspan<T,E,L,A> a) {static_assert(a.rank() == 2);for(int i=0; i<a.extent(0); i++)for(int j=0; j<a.extent(1); j++)a[i,j] = 0;
}// zero out just the surface
template<class T, class E, class L, class A>
void zero_surface(mdspan<T,E,L,A> grid3d) {static_assert(grid3d.rank() == 3);zero_2d(submdspan(grid3d, 0, full_extent, full_extent));zero_2d(submdspan(grid3d, full_extent, 0, full_extent));zero_2d(submdspan(grid3d, full_extent, full_extent, 0));zero_2d(submdspan(grid3d, grid3d.extent(0)-1, full_extent, full_extent));zero_2d(submdspan(grid3d, full_extent, grid3d.extent(1)-1, full_extent));zero_2d(submdspan(grid3d, full_extent, full_extent, grid3d.extent(2)-1));
}
说明:这个例子是提案上的例子
更多的细节需要等标准出来才能确定,大家有兴趣可以持续跟进!
四、总结
其实在看别的资料时发现C++26中还有一个提案是std::mdarray,但在主流的技术支持页面未发现,不知道是否最终会加进来。不过从标准发展的历程来看,这个很有可能会成为标准的一部分。技术以人为本,就是为了让开发者在灵活多样的应用中能够获得标准的接口。也就是“稳定中求灵活,以灵活促稳定”。