Learn Kokkos Module 2:Views & Spaces
1. 核心多维数据抽象:Kokkos::View
1.1 物理结构与生命周期
结构本质
-
轻量级结构体:
- 指针(data pointer)
- 维度信息(extents)
- 步长(stride)
生命周期
-
使用引用计数(类似
std::shared_ptr) -
拷贝行为:
- 浅拷贝(Shallow Copy)
- 仅复制元数据,不复制数据
工程约束
- 所有 View 必须在
Kokkos::finalize()前析构 - 否则会触发内存泄漏断言
1.2 维度规范(Rank 0 ~ 8)
两类维度
-
编译时维度(Static Extent)
- 写在模板中
- 可被编译器优化
-
运行时维度(Dynamic Extent)
- 使用
* - 在构造时传入
- 使用
规则(重要)
- 所有
*必须写在前面
// 正确写法
Kokkos::View<double*[N1][N2]> view("label", N0);
1.3 捕获语义(Capture Semantics)
必须使用
[=] 或 KOKKOS_LAMBDA
原因
-
Device 无法访问 Host 栈变量
-
[&]会导致:- GPU 访问非法内存
- Segfault
性能说明
- View 是浅拷贝
- 按值捕获开销极小(几十字节)
2. 硬件资源拓扑:Spaces
Kokkos 解耦:
- 计算在哪执行(Execution)
- 数据存在哪(Memory)
2.1 Execution Spaces(执行空间)
定义计算发生位置:
Kokkos::SerialKokkos::OpenMPKokkos::Cuda
跨平台修饰符
- 函数:
KOKKOS_INLINE_FUNCTION
- Lambda:
KOKKOS_LAMBDA
2.2 Memory Spaces(内存空间)
定义数据存储位置:
Kokkos::HostSpaceKokkos::CudaSpace
默认机制
- 未指定时:
→ 自动匹配 Execution Space 的默认 Memory Space
3. 跨设备数据同步:Mirroring & Deep Copy
3.1 UVM(统一虚拟内存)
Kokkos::CudaUVMSpace
特点
- 自动迁移(Page Fault)
问题
- 首次访问延迟极高
- 带宽严重下降
结论
- ❌ 高性能代码禁止使用
- ✔ 仅用于快速迁移旧代码
3.2 标准镜像模式(推荐)
步骤
// 1. Device 分配
Kokkos::View<double*, Kokkos::CudaSpace> dev_view("dev", N);
// 2. 创建 Host 镜像
auto host_view = Kokkos::create_mirror_view(dev_view);
// 3. Host 初始化
// ... 填充 host_view
// 4. 显式拷贝
Kokkos::deep_copy(dev_view, host_view);
3.3 自适应优化机制
-
如果原数据在 Host:
create_mirror_view→ 浅拷贝deep_copy→ No-op
效果
- CPU-only 环境无额外开销
4. 性能可移植性基石:Layouts
4.1 CPU vs GPU 访存差异
| 架构 | 优化目标 | 最优布局 | 连续维度 |
|---|---|---|---|
| CPU | Cache | LayoutRight | 最右维 |
| GPU | Coalescing | LayoutLeft | 最左维 |
4.2 自动优化规则(核心)
Kokkos 默认策略:
- Host →
LayoutRight - GPU →
LayoutLeft
4.3 编码黄金法则
始终将线程索引映射到第一个维度
Kokkos::parallel_for("Kernel", N, KOKKOS_LAMBDA(const int i) {
a(i, j, k) = b(i, j, k);
});
效果
- CPU:cache 命中高
- GPU:访存合并
5. 高阶并行归约:Advanced Reductions
5.1 Reducer 基础
内置:
Kokkos::SumKokkos::MinKokkos::MaxKokkos::Prod
5.2 语义一致性(关键)
Lambda 内逻辑必须匹配 Reducer:
double max_val = 0;
Kokkos::parallel_reduce("FindMax", N,
KOKKOS_LAMBDA(const int i, double& lmax) {
if (data(i) > lmax)
lmax = data(i);
},
Kokkos::Max<double>(max_val)
);
5.3 复合归约(MinLoc / MaxLoc)
同时获取:
- 极值
- 索引
Kokkos::MinLoc<double, int>::value_type result;
Kokkos::parallel_reduce("FindMinLoc", N,
KOKKOS_LAMBDA(const int i,
Kokkos::MinLoc<double, int>::value_type& lminloc) {
if (data(i) < lminloc.val) {
lminloc.val = data(i);
lminloc.loc = i;
}
},
Kokkos::MinLoc<double, int>(result)
);
5.4 异步归约(重要优化点)
情况一:标量(同步阻塞)
double sum;
parallel_reduce(..., sum); // Host 会等待
情况二:View(可能异步)
Kokkos::View<double, Kokkos::CudaSpace> d_sum("sum_d");
Kokkos::parallel_reduce(
"AsyncSum",
N,
Functor,
Kokkos::Sum<double, Kokkos::CudaSpace>(d_sum)
);
优势
- Host 不阻塞
- 可与计算重叠
总结(工程要点)
-
View:
- 浅拷贝 + 引用计数
- 生命周期必须早于 finalize
-
Lambda:
- 必须值捕获
-
Spaces:
- Execution ≠ Memory
-
数据同步:
- 禁用 UVM
- 使用 mirror + deep_copy
-
Layout:
- WorkIndex → 第一个维度(核心规则)
-
Reduction:
- Reducer 与逻辑必须一致
- View 版本可实现异步