Vortex CUDA 生态适配:让 CUDA C、CUTLASS 与 Triton 在 RISC-V GPGPU 上运行

Vortex CUDA 生态适配:让 CUDA C、CUTLASS 与 Triton 在 RISC-V GPGPU 上运行

术语约定(全文通用):

SIMT = Vortex 的 SIMT/向量 ALU 通路(VPU,32 lane/warp);

TCU = Tensor Compute Unit(张量计算单元,TFR 型,对应 NVIDIA Tensor Core);

DXA = Vortex 的张量拷贝/DMA 引擎(对应 NVIDIA 的 TMA / cp.async.bulk)。


一、总览:为什么要把 CUDA 生态搬到 Vortex

1.1 Vortex 是什么

Vortex 是一台 RISC-V GPGPU,拥有三条与 NVIDIA 概念对齐的计算/搬运通路:

CUDA 概念 Vortex 对应 配置
warp(32 thread) 一个 SIMT lane 组 NUM_THREADS=32(SIMD_WIDTH)
CTA / thread block Vortex CTA / cluster(sync group) 一个 CTA 最多含 NUM_WARPS=8 个 warp
Tensor Core(mma/wgmma) TCU(TFR 型) NUM_TCU_LANES=32;TF32/FP16/BF16/FP8/INT8/MX 独立使能
TMA / cp.async.bulk DXA 张量拷贝引擎 NUM_DXA_UNITS、DXA_QUEUE_SIZE=16;支持 1--5 维 + multicast
shared memory LMEM LMEM_LOG_SIZE=14(16 KB),32 bank
warp 内 shfl vx_shfl_{bfly,idx,up,down} 真实指令,非 no-op

三条通路的分工:

  • SIMT(VPU/ALU):通用逐元素向量 FMA,32 lane 锁步;承载普通 kernel、SIMT GEMM、epilogue 算术。
  • TCU:张量数学,承载 TensorOp/WGMMA/MX/稀疏的乘累加,分 WMMA(单 warp)与 WGMMA(warp-group)两档。
  • DXA :gmem↔smem 的 N 维 tile 搬运,与 TCU 计算重叠(双缓冲/多段流水线)。

1.2 为什么是 CUDA 生态

GEMM/卷积/注意力是 GPGPU 工作负载的主体,而整个软件栈围绕 CUDA 生态构建:

  • CUDA C 是底层开发语言;
  • CUTLASS 是最成熟、覆盖最全的 GEMM/Conv/Attention 模板库(NVIDIA 官方,SM70→SM100),重写一套等价模板的代价极高且永远追不上其演进;
  • Triton 是 Python 侧 GPU kernel DSL,是 PyTorch 生态大量算子的实现底座。

因此 Vortex 适配的目标是:让现有 CUDA 生态代码------CUDA C 源码、CUTLASS 模板、Triton kernel------不改(或极小改)即可在 Vortex 上编译、运行、数值正确。三类用户、三套入口,共享同一套底座。

1.3 三条适配路线一览

路线 用户 核心工作 验证
CUDA C 手写 kernel 的开发者 CUDA 兼容层 + clang chevron/kargs ABI + 分支分歧 pass 向量加法 demo,bit-exact
CUTLASS C++ 模板库用户 在模板 guard 层把 mma/cp.async/TMA 映射到 TCU/DXA,stock 路径不动 35 个自校验特性测试,one_fast G5 PASS 35/35
Triton Python kernel 开发者 标准 Triton 后端(TTGIR→LLVM IR emitter)+ torch privateuse1 设备 simx + rtlsim 双驱动验收通过,FA forward 全规模(rel-L2 ≈ 2.4e-4)

三者共用同一底座:Vortex LLVM fork(vxcc)+ libvortex.so 运行时 + CUDA 兼容层。


二、公共底座:工具链、CUDA 兼容层与启动 ABI

本章是三条路线的公共前提。前置:Vortex 工具链与运行时已构建(./one.sh 或 ./one_fast.sh 跑过一次)。

下文统一假设 VORTEX_HOME=/data/vortex,交付树 /data/vortex/llvm_vortex_release(含 vxcc + 工具链库),运行时库 /data/vortex/build/sw/runtime/libvortex.so。

2.1 工具链与运行时

  • vxcc(Vortex LLVM fork):把 CUDA C++ / LLVM IR 编译成 host 可执行 + 设备镜像。
  • libvortex.so :模块加载/卸载(vx_* 模块 API)、vx_enqueue_launch(uniform args struct 启动 ABI)、DXA 描述符编程、lmem 尺寸查询。Vortex 是单地址空间设备内存,cudaMalloc/cudaMemcpy 直接落在其上。
  • 驱动选择 :VORTEX_DRIVER=simx(C++ 功能仿真,快,默认)或 rtlsim(Verilator 周期精确,慢一个数量级以上,按需)。

环境变量陷阱 :LD_LIBRARY_PATH 必须把 Vortex 工具链/运行时库目录放在最前------clang 的 RUNPATH 只有 $ORIGIN/../lib,而 LD_LIBRARY_PATH 优先于 RUNPATH;若 shell 里残留旧工具链的 lib 目录,会加载到旧 libLLVM,产生 clang -cc1as: error: unknown target triple 'unknown' 之类的诡异错误:

bash 复制代码
export VORTEX_HOME=/data/vortex
export PATH="/data/vortex/llvm_vortex_release/bin:$PATH"
export LD_LIBRARY_PATH="/data/vortex/llvm_vortex_release/lib:/data/vortex/build/sw/runtime:$LD_LIBRARY_PATH"

2.2 CUDA 兼容层的四个层面

CUDA 兼容层(sw/kernel/include/*.h + libvortex.so + 工具链)覆盖 CUDA C++ 所需的全部接口面:

  1. 执行空间与索引 :__host__ __device__ __global__ __forceinline__ __noinline__ __launch_bounds__ __shared__ 全部定义;blockIdx/blockDim/threadIdx/gridDim/warpSize 映射到 Vortex CTA/lane 索引(warpSize=32)。
  2. warp 内禀(真实现,非 no-op) :__syncthreads(CTA barrier)、__syncwarp、__shfl_{up,down,idx,xor,_sync}、__ballot、__any、__all → 落到 vx_shfl_{bfly,idx,up,down} 等真实指令。CUTLASS 的 epilogue / FA 依赖 shfl 做跨 lane reduce。
  3. 运行时 API :cudaMalloc/cudaFree/cudaMemcpy/cudaMemcpy2D/cudaMemset/cudaDeviceSynchronize/cudaGetDevice(Properties)/cudaSetDevice/cudaGetErrorString、cudaError_t/cudaSuccess;cudaGetDeviceProperties 返回 Vortex 配置值(warpSize=32 等),使 CUTLASS 的设备探测路径走通。
  4. vector 类型 :float2/3/4、int2/3/4 + make_* 工厂(冲突项统一改名为 vx_make_*,以避开兼容层自身的 make_* 宏)。

2.3 kernel 启动与参数 ABI:chevron 与 kargs

两个关键的"跨层"机制决定了 CUDA 源码可以零改动:

  • chevron 启动 kernel<<<grid,block,smem>>>(args) :由 clang Sema 的 ActOnVortexKernelLaunch 解析,生成 host 桩代码(经 libvortex.so 启动);host 桩把参数打包为单个 kargs 指针 ,设备入口处的参数再由 RISCVVortexKernelArgUnpack pass 解包还原。CUDA C++ 源码因此零改动(无需手写 kargs 结构体)。
  • SIMT 分支分歧 :VortexBranchDivergence pass 把 warp 内 32-lane 的分支分歧代码转成 per-lane 谓词执行(SPLIT-JOIN),保证普通 CUDA if/else 在 SIMT 模型下语义正确。

设备侧还有配套 math 库:exp2f/sqrtf/fmaf/... 以 C-linkage extern 声明,链接时从 libvxmath.a 解析(数值已验证,含 inf/0)。

2.4 SIMT / TCU / DXA:三条数据通路的统一视角

所有 CUDA 生态代码最终都落到这三条通路:

角色 实现机制(ISA 指令 / 编译器 pass / 引擎)
通用计算 / SIMT FMA mainloop RISC-V VPU/ALU 向量 FMA(32-lane)
分支分歧 VortexBranchDivergence pass(SPLIT/JOIN 谓词)
WMMA(单 warp 张量) __vortex_mma_16x16x16(经 wmma_context::mma_sync)
WGMMA(多 warp 张量) wgmma_sync(SS/RS 两形,warp-group 锁步)
gmem→smem 取数 vx_dxa_issue_{1..5}d_{multicast}_wg(DXA,一次发射一条 tile 拷贝)
流水线会合 vortex::barrier expect_tx / arrive_and_wait(mbarrier 类比)
原子 RISC-V A 扩展 AMO(AMOADD/AMOSWAP/AMOMIN/MAX + LR/SC)

三、路线一:用 CUDA C 直接开发(基础入口)

3.1 最小 demo:向量加法

一个最小的纯 CUDA C 向量加法 out[i] = a[i] + b[i]。kernel 是标准 CUDA C ------普通多参 __global__ 函数,以 chevron 语法启动,没有任何 Vortex 专有代码,源码与写给 NVIDIA GPU 的完全一致:

c++ 复制代码
// main.cu
#include <cuda_runtime.h>
#include <cstdio>

__global__ void vecAdd(const float* a, const float* b, float* out, int n) {
  int i = blockIdx.x * blockDim.x + threadIdx.x;
  if (i < n)
    out[i] = a[i] + b[i];
}

#define CHECK_CU(expr)                                                  \
  do {                                                                  \
    cudaError_t _e = (expr);                                            \
    if (_e != cudaSuccess) {                                            \
      std::printf("FAIL %s:%d: %s -> %s\n", __FILE__, __LINE__,         \
                  #expr, cudaGetErrorString(_e));                       \
      std::exit(1);                                                     \
    }                                                                   \
  } while (0)

int main() {
  const int n = 4096;
  float* h_a = new float[n];
  float* h_b = new float[n];
  float* h_out = new float[n];
  for (int i = 0; i < n; ++i) {
    h_a[i] = (float)(i % 97);
    h_b[i] = (float)((i * 3) % 89);
  }

  float *d_a, *d_b, *d_out;
  CHECK_CU(cudaMalloc((void**)&d_a, n * sizeof(float)));
  CHECK_CU(cudaMalloc((void**)&d_b, n * sizeof(float)));
  CHECK_CU(cudaMalloc((void**)&d_out, n * sizeof(float)));
  CHECK_CU(cudaMemcpy(d_a, h_a, n * sizeof(float), cudaMemcpyHostToDevice));
  CHECK_CU(cudaMemcpy(d_b, h_b, n * sizeof(float), cudaMemcpyHostToDevice));

  const int block = 128;
  const int grid = (n + block - 1) / block;
  vecAdd<<<grid, block>>>(d_a, d_b, d_out, n);
  CHECK_CU(cudaDeviceSynchronize());

  CHECK_CU(cudaMemcpy(h_out, d_out, n * sizeof(float), cudaMemcpyDeviceToHost));

  int bad = 0;
  for (int i = 0; i < n; ++i)
    if (h_out[i] != h_a[i] + h_b[i]) ++bad;
  if (bad) { std::printf("FAIL: %d/%d elements wrong\n", bad, n); return 1; }
  std::printf("PASS: cuda-vecadd %d elements\n", n);

  CHECK_CU(cudaFree(d_a)); CHECK_CU(cudaFree(d_b)); CHECK_CU(cudaFree(d_out));
  delete[] h_a; delete[] h_b; delete[] h_out;
  return 0;
}

3.2 编译与运行

bash 复制代码
# 环境变量见 2.1;纯 CUDA C 无需额外头目录
export VORTEX_HOME=/data/vortex
export PATH="/data/vortex/llvm_vortex_release/bin:$PATH"
export LD_LIBRARY_PATH="/data/vortex/llvm_vortex_release/lib:/data/vortex/build/sw/runtime:$LD_LIBRARY_PATH"

# vxcc 编译;最后一个参数是 libvortex.so(vxcc 据此生成 host 桩代码)
vxcc -o main main.cu /data/vortex/build/sw/runtime/libvortex.so

# 运行
VORTEX_DRIVER=simx ./main
# 预期输出:
# launching vecAdd: grid=32 block=128 n=4096
# PASS: cuda-vecadd 4096 elements

这一条路线是其他两条的基线:CUDA 兼容层 + chevron ABI + 分支分歧 pass 全部就位后,CUTLASS 和 Triton 才有落点。


四、路线二:用 CUTLASS 开发(复用模板库)

4.1 最小 demo:SIMT GEMM

D = alpha*A*B + beta*C,标准 CUTLASS 写法,零 Vortex 专有代码:

c++ 复制代码
// cutlass_main.cu
#include <cmath>
#include <cstdio>
#include <vector>
#include <cuda_runtime.h>
#include "cutlass/cutlass.h"
#include "cutlass/layout/matrix.h"
#include "cutlass/gemm/device/gemm.h"

#define CUDA_CHECK(call)                                                     \
  do {                                                                       \
    cudaError_t e_ = (call);                                                 \
    if (e_ != cudaSuccess) {                                                 \
      std::printf("CUDA error: %s (line %d)\n", cudaGetErrorString(e_),      \
                  __LINE__);                                                 \
      std::exit(1);                                                          \
    }                                                                        \
  } while (0)

// SIMT (CUDA-core) GEMM, f32 in / f32 out, threadblock tile 32x64x8
using Gemm = cutlass::gemm::device::Gemm<
    float, cutlass::layout::RowMajor,      // A  [M,K] row-major
    float, cutlass::layout::ColumnMajor,   // B  [K,N] column-major
    float, cutlass::layout::RowMajor,      // C/D [M,N] row-major
    float,                                 // ElementAccumulator
    cutlass::arch::OpClassSimt,
    cutlass::arch::Sm80,
    cutlass::gemm::GemmShape<32, 64, 8>>;

int main() {
  const int M = 8, N = 8, K = 8;
  const float alpha = 1.0f, beta = 0.0f;

  std::vector<float> hA(size_t(M) * K), hB(size_t(N) * K), hC(size_t(M) * N, 0.0f);
  for (int i = 0; i < M * K; ++i) hA[i] = 0.1f * float(i);
  for (int i = 0; i < N * K; ++i) hB[i] = 0.05f * float(i);

  // CPU reference: D[m][n] = sum_k A[m][k]*B[k][n]
  std::vector<float> ref(size_t(M) * N);
  for (int m = 0; m < M; ++m)
    for (int n = 0; n < N; ++n) {
      float acc = 0.0f;
      for (int k = 0; k < K; ++k) acc += hA[m * K + k] * hB[n * K + k];
      ref[m * N + n] = acc;
    }

  float *dA, *dB, *dC;
  CUDA_CHECK(cudaMalloc(&dA, sizeof(float) * M * K));
  CUDA_CHECK(cudaMalloc(&dB, sizeof(float) * N * K));
  CUDA_CHECK(cudaMalloc(&dC, sizeof(float) * M * N));
  CUDA_CHECK(cudaMemcpy(dA, hA.data(), sizeof(float) * M * K, cudaMemcpyHostToDevice));
  CUDA_CHECK(cudaMemcpy(dB, hB.data(), sizeof(float) * N * K, cudaMemcpyHostToDevice));
  CUDA_CHECK(cudaMemcpy(dC, hC.data(), sizeof(float) * M * N, cudaMemcpyHostToDevice));

  Gemm op;
  Gemm::Arguments args{
      {M, N, K},
      {dA, Gemm::LayoutA(K)},
      {dB, Gemm::LayoutB(K)},
      {dC, Gemm::LayoutC(N)},
      {dC, Gemm::LayoutC(N)},
      {alpha, beta},
      1};  // split_k
  size_t ws = Gemm::get_workspace_size(args);
  void* dws = nullptr;
  if (ws) CUDA_CHECK(cudaMalloc(&dws, ws));
  if (op.initialize(args, dws) != cutlass::Status::kSuccess) {
    std::printf("FAIL: initialize\n");
    return 1;
  }
  if (op() != cutlass::Status::kSuccess) {
    std::printf("FAIL: run (CUDA: %s)\n", cudaGetErrorString(cudaGetLastError()));
    return 1;
  }
  CUDA_CHECK(cudaDeviceSynchronize());

  std::vector<float> got(size_t(M) * N);
  CUDA_CHECK(cudaMemcpy(got.data(), dC, sizeof(float) * M * N, cudaMemcpyDeviceToHost));
  bool ok = true;
  for (int i = 0; i < M * N; ++i)
    if (std::fabs(ref[i] - got[i]) > 1e-4f) ok = false;
  if (!ok) {
    std::printf("FAIL: D[0][0] expected %.6f got %.6f\n", ref[0], got[0]);
    return 1;
  }
  std::printf("PASS: gemm %dx%dx%d  D[0][0]=%.4f\n", M, N, K, got[0]);
  return 0;
}
bash 复制代码
export LD_LIBRARY_PATH=/data/vortex/llvm_vortex_release/lib:/data/vortex/build/sw/runtime

# vxcc 编译;-isystem 指定 cutlass 核心头目录 + tools/util
/data/vortex/llvm_vortex_release/bin/vxcc \
    --llvm-path /data/vortex/llvm_vortex_release \
    --conf /data/vortex/llvm_vortex_release/conf/vxcc.conf \
    -isystem/data/vortex/cutlass/include \
    -isystem/data/vortex/cutlass/tools/util/include \
    -o cutlass_main cutlass_main.cu /data/vortex/build/sw/runtime/libvortex.so

VORTEX_DRIVER=simx ./cutlass_main
# 预期输出:
# PASS: gemm 8x8x8  D[0][0]=0.7000

4.2 适配原理:模板层 guard 换分支,stock 路径不动

CUTLASS 的 kernel 模板按 OpClass 选择数据通路。适配的做法是:在模板层 guard 的 Vortex 分支里,把"SM70/SM80 的 mma/cp.async 内禀"替换为"Vortex 的 TCU/DXA 指令",stock CUDA 代码路径完全不动。

SIMT 通路 :OpClassSimt 的 stock SIMT mainloop 原样保留------它本就是逐元素 FMA,直接落到 RISC-V VPU/ALU 的向量 FMA,不需要 Vortex 专属内禀。对应测试 f01(f32)、f06(f64),数值 bit-exact。

TCU 通路(张量数学,分四档):

  1. WMMA(默认 TensorOp) :stock SM70/SM80 mma 片段经 wmma_context::mma_sync(做 bank-conflict-free 的寄存器重排)发射 __vortex_mma_16x16x16(16×16 TCU tile,每 stage 仅发一次 ,片段打包/拆包走 vx_pack_wmma_operand / vx_acc_bits_to_float)。不再走 PTX mma.sync(mma_tensor_op_vortex.h 内 0 处 PTX mma)。类型覆盖:f16×f16→f32、bf16→f32、tf32、s8/uint8→s32、narrow-acc、mixed-input。
  2. WGMMA :Vortex 的 Hopper wgmma 类比------wgmma_sync 指令(SS 形 wgmma_sync(fragD, desc_a, desc_b, fragC) / RS 形 wgmma_sync(fragD, fragA, desc_b, fragC),NRC=8/16/32),warp-group(ISSUE_WIDTH 个 warp 锁步)中各 warp 计算 threadblock M tile 的一个 16 行切片,并共享 B 操作数(smem 描述符);epilogue 直接把 NRC 片元存入 gmem。这是特性测试里第一个多 warp TCU CTA(f24)。
  3. MX / block-scaled(f28--f31) :mxfp8 / mxbf8 / mxfp4 / nvfp4,经 gemm_mx.h + mx_mma_multistage_vortex.h。
  4. 2:4 结构化稀疏(f27) :完整 cutlass::gemm::device::SparseGemm 路径;2:4 稀疏作用于 A(沿 K 剪枝 → 压缩为 M×K/2),per-group 元数据在 host 端预打包(pack_metadata)后作为不透明 E 操作数喂入;VxSparseMma + wmma_context<..., is_sparse=true>。

DXA 通路(gmem→smem 取数,四段一一映射):

CUDA Vortex 说明
CUtensorMap(host 编程) vortex::dxa::program_2d() host 端编程描述符槽(shape+strides+tile)
cp.async.bulk.tensor(设备) vx_dxa_issue_{1..5}d_{multicast}_wg 1--5 维 tile 拷贝,一次发射
mbarrier vortex::barrier expect_tx / arrive_and_wait 事务计数 + CTA 会合
Hopper TMA mainloop 多段 / 双缓冲 chunked-K 流水线 下一 tile 取数与当前 tile TCU 计算重叠

DXA 的 host 端描述符编程是用户流程里唯一的 Vortex 专属新增 (在 launch 前编程两个操作数描述符);设备端由一个 warp 为每条 tile 发射一条指令,由 DXA AXI master 服务(而非逐 lane 的 LSU store)。

上层机制(3.x) :GemmUniversal 链跑通(GemmUniversalAdapter → kernel::GemmUniversal → CollectiveMmaVortex(gmem-direct TCU mainloop) + CollectiveEpilogueVortex);stock 3.x EVT visitor tree (D = ReLU(βC + α·acc + bias[m]))与 persistent kernel 持久化 tile 调度均可用。

4.3 特性覆盖(f01--f35,35/35 全绿)

A. 标准 CUTLASS / CUDA 特性(stock 模板直接映射):

类别 覆盖 测试
GEMM-SIMT f32 / f64 f01, f06
GEMM-TensorOp(WMMA) f16 / bf16 / tf32 / int8 / uint8 / narrow-acc / beta f02--f05, f19--f21
GEMM-结构 mixed-input / strided-batched / split-K / grouped / bias+ReLU epilogue / horizontal swizzle f07--f11, f22, f23
Conv2d fprop implicit GEMM(NHWC) f12
规约 device-wide tensor reduction f13
CuTe tiled copy / TiledMma GEMM(sm80 MMA atom) f14, f15
Attention Fused Multi-Head Attention forward(GQA,FMA) f16

B. Vortex 专属数据通路(把 NVIDIA 较新/受限特性在 Vortex 硬件模型上重实现):

类别 覆盖 测试
DXA(TMA 类比)mainloop DXA+SIMT / DXA+TCU-WMMA f17, f18
WGMMA(warp-group MMA) wgmma_sync,4-warp CTA,NRC 片元直接存 gmem f24
FP8 / BF8 fp8 / bf8 GEMM f25, f26
2:4 稀疏 int4×int4→int32,VxSparseMma f27
MX / block-scaled mxfp8 / mxbf8 / mxfp4 / nvfp4 f28--f31
CUTLASS 3.x collective / EVT / persistent gmem-direct TCU mainloop / visitor tree / 持久化调度 f32--f34
host API 完成面 streams/events、cudaMemcpy2D、pinned host 内存、cudaFuncGetAttributes、cudaMemGetInfo(真实 TCU kernel 启动前后实测) f35

未覆盖/受限:sm_90+ 的 TMA multicast 多播语义(DXA 硬件有 multicast 指令,但 CUTLASS cluster 级多播 kernel 被 guard 屏蔽)、stock stream-K、blockwise-scaled GEMM(sm_100/sm_89 语义)。


五、路线三:用 Triton 开发(Python 入口)

5.1 最小 demo:vecadd

张量放在 Vortex 设备(torch privateuse1 后端)上,@triton.jit kernel 由 Vortex Triton 后端编译并经 libvortex.so 启动:

python 复制代码
# triton_main.py
import os
os.environ.setdefault("TRITON_DEFAULT_BACKEND", "vortex")

# IMPORTANT: `import triton` BEFORE adding /data/vortex to sys.path,
# otherwise the repo's triton/ dir shadows the installed package.
import triton
import triton.language as tl

import sys
VH = os.environ.get("VORTEX_HOME", "/data/vortex")
sys.path.insert(0, VH)

import torch
import torch_vortex  # noqa: F401  (registers the "vortex" torch device)

@triton.jit
def vecadd_kernel(x_ptr, y_ptr, out_ptr, n, BLOCK: tl.constexpr):
    pid = tl.program_id(0)
    offs = pid * BLOCK + tl.arange(0, BLOCK)
    mask = offs < n
    x = tl.load(x_ptr + offs, mask=mask)
    y = tl.load(y_ptr + offs, mask=mask)
    tl.store(out_ptr + offs, x + y, mask=mask)

def main():
    N = 4096
    BLOCK = 128
    x = torch.arange(N, dtype=torch.float32) * 0.01
    y = torch.arange(N, dtype=torch.float32) * -0.005
    xv = x.to("vortex")
    yv = y.to("vortex")
    out = torch.empty(N, device="vortex")

    grid = (triton.cdiv(N, BLOCK),)
    vecadd_kernel[grid](xv, yv, out, N, BLOCK=BLOCK)
    torch.vortex.synchronize()

    ref = x + y
    got = out.cpu()
    err = (got - ref).abs().max().item()
    print(f"vecadd: max|err| = {err:.3e} (N={N})")
    assert err < 1e-5, f"vecadd mismatch (max|err|={err:.3e})"
    print("PASS: vecadd triton-on-vortex")

if __name__ == "__main__":
    main()
bash 复制代码
# 一键环境文件:VORTEX_HOME / PYTHONPATH / TRITON_DEFAULT_BACKEND=vortex /
#   VORTEX_STARTUP_ADDR / TRITON_BACKENDS_IN_TREE=1
source /data/vortex/triton.env

VORTEX_DRIVER=simx python3 triton_main.py
# 预期输出:
# triton version: 3.8.0
# vecadd: max|err| = 0.000e+00 (N=4096)
# PASS: vecadd triton-on-vortex

5.2 适配原理:标准 Triton 后端

Triton 后端由 python 侧管线(compiler.py/driver.py)+ C++ 侧 TTGIR→LLVM IR emitter(vortex_ir_emit.cc,~3500 行,以 pybind 插件编入 libtriton.so)构成。核心约束:一个 Triton program = 一个 Vortex CTA = 32 lane (num_warps 强制 1,warp_size=32),并行度全部由 grid 承担。

编译链:

复制代码
Python:  @triton.jit kernel + kernel[grid](...)
   ▼  Triton JIT 前端(签名特化、constexpr 折叠、==1 stride DCE)
TTIR → TTGIR("vortex:100", blocked 布局, num_warps=1)
   ▼  make_llir = _vx.emit_llir(TTGIR)         [emitter: 布局分析 + 降级]
   → (.ll 文本, kernel 名, lmem 峰值, 存活参数名, dxa_specs JSON)
   ▼  make_vxbin = _vx.build_kernel_ll(.ll)    [vxcc --device-only -x ir]
kernel.vxbin 字节 → 磁盘缓存(key 含工具链指纹)
   ▼  driver.load_binary → _vx.load_module → module id
VortexLauncher.__call__:
   tf32 重打包(e8m10) → 参数打包(by-value blob) → 每启动前编程 DXA 描述符
   → vx_enqueue_launch                          [libvortex.so]
   ▼
设备执行 → torch.vortex.synchronize() / 拷回

SIMT 通用路径 :load/store → 逐 lane RISC-V L/S;tt.dot 通用路径 = 操作数 fragment 经 lmem staging 、逐 lane fmaf 累加;convert_layout → lmem 往返(row-broadcast 有快路径);跨 lane 规约 → lmem scratch combine;原子 → A 扩展 AMO;gather/scatter(Vortex 无任意索引 gather)→ 逐 lane 寻址 primitive。

TCU + DXA 路径(GEMM 模式匹配) :emitter 内置一个 GEMM 匹配器,标准 matmul 满足以下条件时接管整个 kernel,发射 TCU WGMMA + DXA 双缓冲流水线:

  • BLOCK_M = 16(= WGMMA xtileM);BLOCK_N ∈ {16, 32, 64}(= 2×NRC);
  • 单个 scf.for over k,零初始化 [16, BLOCK_N] 累加器(f32/f16/i32);
  • BLOCK_K = 该 dtype 的 WGMMA tileK:fp16/bf16→16,int8/fp8→32,tf32→8;
  • 9/12 参数标准形状;若内层 stride 存活(非稠密矩阵)则安全回落到通用 FMA 路径。

命中后:tt.dot → TCU WGMMA(SS 形) (A/B 均从 smem 描述符读,acc 在 f 寄存器读改写);DXA 描述符由 emitter 以 JSON spec 随编译产物带出,launcher 每次启动前 按实际 M/N/K/strides/指针调 dxa_program_2d 编程;2 个 DMA stage 双缓冲 ------后续 tile 的 DXA 取数提前发出,使搬运与当前 tile 的 WGMMA 计算结构性重叠;wstall 语义保证同一 stage buffer 只会在其上一次 wgmma 全部退休后才被下一轮 DMA 覆写,因此无需额外的尾部 barrier。

tf32 特例 :Vortex TCU 的 tf32 输入格式是 e8m10 (1+8+10 bit,低 19 bit 打包)而非 IEEE tf32 位型;launcher 在启动前把 fp32 输入在 host 侧重新打包为 e8m10(bit-exact 对齐 C 端 cvt_f32_to_custom)。

torch 集成 :torch_vortex 以 RegisterPrivateUse1HooksInterface 注册 "vortex" 设备------x.to("vortex") 分配设备内存并拷入、out.cpu() 走 d2h、torch.vortex.synchronize() 同步。

工程保障 :vxbin 镜像不可重定位,每 kernel 按源码 sha256 分配独立地址槽(0x180000000 + (hash % 4096) * 64KiB,256 MiB 跨度,跨进程稳定);磁盘缓存 key 对全部设备构建输入取 mtime+size 指纹,工具链重建后自动失效;VORTEX_VXCC_KEEP=1 保留中间产物便于排障。

5.3 特性覆盖与验证

特性 说明
逐元素 / 掩码 load/store、2D tile 拷贝 vecadd / copy2d
FA 数值核心路径 PV dot + l-reduce + normalize(pvdot 隔离测试)
gather/scatter、原子(5 种) 逐 lane primitive / A 扩展 AMO
TCU WGMMA + DXA GEMM 标准 matmul,fp16/bf16/int8/fp8/tf32
FA forward(GQA,f16) 完整 online-softmax 注意力,全规模 PASS(rel-L2 ≈ 2.4e-4)
设备 math、跨 lane 规约、布局转换 libvxmath.a / lmem combine / lmem 往返

test_triton.sh [quick|fa|full] --driver=simx|rtlsim 双驱动验收,simx 全规模 PASS(含 FA-512),rtlsim 周期精确仿真数值与 simx 逐位一致。


六、三条路线横向对比与全局验证

6.1 横向对比

维度 CUDA C CUTLASS Triton
用户写什么 标准 CUDA C,chevron 启动 标准 CUTLASS 模板实例化 @triton.jit + torch "vortex" 设备
源码改动 用户源码零改动 用户源码零改动(适配改动全在 guard 的 Vortex 分支,新增 16 个 vortex_* 头) 用户源码零改动(需 TRITON_DEFAULT_BACKEND=vortex + import torch_vortex)
张量通路 SIMT(全部) SIMT/TCU(WMMA+WGMMA+MX+稀疏)/DXA SIMT(通用)/TCU WGMMA(标准 matmul 模式)
取数通路 逐 lane L/S DXA(TMA 类比,1--5 维) 逐 lane L/S;GEMM 模式走 DXA 双缓冲
Vortex 专属用户动作 无 TCU/DXA 路径需在 launch 前编程 DXA 描述符 无(描述符由 launcher 自动编程)
核心限制 --- cluster 级 TMA 多播 / stream-K / blockwise-scaled 未启用 num_warps≡1;GEMM 走 TCU 需满足固定 tile 模式

6.2 全局验证与门控

  • CUDA 兼容:chevron/kargs/warp 内禀等由 CUDA C 路径与各 demo 覆盖。
  • CUTLASS :minimal_feature_tests/run_vortex.sh 并行编译 + 运行 35 个单文件自校验测试,输出 MFT simx: PASS 35/35 (fail=0 build-fail=0);每个测试对 CPU 参考值数值比对,正确性内建。
  • Triton :test_triton.sh simx + rtlsim 双驱动,任意 mode×驱动 ≤2h 预算(FA-512 为显式长任务);开发期用 pvdot/copy2d 等最小 kernel 隔离数值问题。
  • 门控 :三条路线的验证套件均纳入 one_fast.sh 全矩阵分组(CUDA 兼容、Triton G3/G4、CUTLASS G5),全绿为发布条件。

6.3 小结

维度 结论
底座 Vortex LLVM fork(vxcc)+ libvortex.so 运行时 + CUDA 兼容层(执行空间/warp 内禀/运行时 API/chevron kargs ABI/分支分歧),三路线共用
数据通路 SIMT(向量 FMA)/ TCU(WMMA、WGMMA、MX、2:4 稀疏)/ DXA(1--5 维 + multicast 指令,TMA 类比)
生态覆盖 CUDA C 零改动;CUTLASS GEMM/Conv/Attention/CuTe/3.x collective/EVT/persistent;Triton 通用 kernel + 标准 matmul TCU 加速 + FA forward 全规模
验证 35/35 CUTLASS 特性测试 + Triton 双驱动套件 + FA 全规模数值验收,一键门控全绿
相关推荐
龙腾AI白云4 小时前
AI检索增强生成(RAG):解决大模型幻觉的核心落地技术
数据库·人工智能·机器学习·知识图谱
云票4 小时前
企业对接AI合同审查系统的工程实践
人工智能
admin and root4 小时前
「AI安全篇」实战AntiDebug自动化JS逆向加解密MCP
javascript·人工智能·网络安全·自动化·漏洞挖掘·cnvd·src赏金
智能RPA4 小时前
智能体自动化平台与主数据管理平台(MDM)对比评测
人工智能·自动化·agent·rpa
跨境小彭5 小时前
Temu拉美站点铺货实操复盘:手动复制痛点与批量自动化解决方案
服务器·人工智能·搜索引擎·自动化·temu电商运营
封印师请假去地球钓鱼5 小时前
边解边变的问题:从“决策依赖“一词出发
人工智能·算法
AI搅拌机6 小时前
ComfyUI管理大师:安全稳定升级+切换指定版本!
人工智能
浅安的邂逅6 小时前
20929-OpenAI 一天踩三脚急刹:暂停前沿训练、叫停 Astra、披露越权访问澳政府网站
人工智能·大模型·ai编程·行业动态·ai日报
Qyr996 小时前
2026-2032直接芯片液冷板市场爆发式增长:AI算力浪潮下的热管理核心赛道
大数据·人工智能