Learn Kokkos Module 2:Views & Spaces

3 阅读3分钟

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::Serial
  • Kokkos::OpenMP
  • Kokkos::Cuda

跨平台修饰符

  • 函数:
KOKKOS_INLINE_FUNCTION
  • Lambda:
KOKKOS_LAMBDA

2.2 Memory Spaces(内存空间)

定义数据存储位置:

  • Kokkos::HostSpace
  • Kokkos::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 访存差异

架构优化目标最优布局连续维度
CPUCacheLayoutRight最右维
GPUCoalescingLayoutLeft最左维

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::Sum
  • Kokkos::Min
  • Kokkos::Max
  • Kokkos::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 版本可实现异步