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++ 所需的全部接口面:
- 执行空间与索引 :
__host__ __device__ __global__ __forceinline__ __noinline__ __launch_bounds__ __shared__全部定义;blockIdx/blockDim/threadIdx/gridDim/warpSize映射到 Vortex CTA/lane 索引(warpSize=32)。 - 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。 - 运行时 API :
cudaMalloc/cudaFree/cudaMemcpy/cudaMemcpy2D/cudaMemset/cudaDeviceSynchronize/cudaGetDevice(Properties)/cudaSetDevice/cudaGetErrorString、cudaError_t/cudaSuccess;cudaGetDeviceProperties返回 Vortex 配置值(warpSize=32 等),使 CUTLASS 的设备探测路径走通。 - 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 指针 ,设备入口处的参数再由RISCVVortexKernelArgUnpackpass 解包还原。CUDA C++ 源码因此零改动(无需手写 kargs 结构体)。 - SIMT 分支分歧 :
VortexBranchDivergencepass 把 warp 内 32-lane 的分支分歧代码转成 per-lane 谓词执行(SPLIT-JOIN),保证普通 CUDAif/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 通路(张量数学,分四档):
- 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)。不再走 PTXmma.sync(mma_tensor_op_vortex.h内 0 处 PTX mma)。类型覆盖:f16×f16→f32、bf16→f32、tf32、s8/uint8→s32、narrow-acc、mixed-input。 - 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)。 - MX / block-scaled(f28--f31) :mxfp8 / mxbf8 / mxfp4 / nvfp4,经
gemm_mx.h+mx_mma_multistage_vortex.h。 - 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.forover 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.shsimx + 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 全规模数值验收,一键门控全绿 |