Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions docs/REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# LLAISYS 多平台适配报告(Assignment #4)

## 一、支持的平台及其状态

| 平台 | 厂商 | 设备/环境 | 状态 |
|------|------|-----------|------|
| NVIDIA | NVIDIA | CUDA SDK(CUDA 13.3) | ✅ 已支持,runtime / 算子 / 推理均通过 |
| 天数(SUDA) | Iluvatar(天数智芯) | CoreX 4.4.0,GPU `Iluvatar BI-V150`(32GB) | ✅ 本次新增,runtime / 算子 / 推理均通过 |

> 平台选择方式:通过 `--device {cpu,nvidia,suda}` 显式指定设备类型。
>
> 关于天数平台:CoreX SDK 对外提供 **CUDA 源码级兼容层**(`cuda_runtime.h` / `cublas_v2.h` / `nvcc` / `libcudart.so` / `libcublas.so`),**没有独立的 `suda_runtime.h` / `ixblas.h` API**。因此天数后端实现为「复用 CUDA API + 独立 `suda` 命名空间/目录/device type」。

## 二、复现流程

### 通用前置
- 编译工具:Xmake、g++/gcc、Python ≥ 3.9、torch、transformers、safetensors、huggingface_hub
- 模型:`deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B`

### 2.1 NVIDIA 平台
```bash
xmake f --nv-gpu=y -c
xmake -y
xmake install
python3 test/test_runtime.py --device nvidia
python3 test/ops/add.py --device nvidia # 及其余 7 个算子
python3 test/test_infer.py --model <model_dir> --test --device nvidia
```

### 2.2 天数(SUDA)平台
```bash
# 1) 配置环境变量(平台已安装 xmake v2.8.7 / python3 / torch 2.7.1)
export PATH=/usr/local/corex-4.4.0/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/corex-4.4.0/lib64:/usr/local/corex-4.4.0/lib:$LD_LIBRARY_PATH
export CUDA_PATH=/usr/local/corex-4.4.0
export XMAKE_ROOT=y

# 2) 修补 CoreX 的 nvcc wrapper(解析 xmake 的 -Xcompiler/-Werror 等参数)
# 用临时 wrapbin/nvcc 覆盖 /usr/local/corex-4.4.0/bin/nvcc

# 3) 补 libcudadevrt 软链(CoreX 不提供该库,本项目不使用动态并行)
ln -sf libcudart.so /usr/local/corex-4.4.0/lib64/libcudadevrt.so

# 4) 编译 + 安装
xmake f --suda-gpu=y -c
xmake -y
xmake install # 复制 libllaisys.so 到 python/llaisys/libllaisys/

# 5) 测试
export PYTHONPATH=<repo>/python:$PYTHONPATH
python3 test/test_runtime.py --device suda
python3 test/ops/<op>.py --device suda # add/argmax/embedding/linear/rms_norm/rope/self_attention/swiglu
python3 test/test_qwen2_load.py <model_dir> suda
```

## 三、复现结果

### 3.1 天数(SUDA)平台实测结果(BI-V150)

| 测试项 | 结果 |
|--------|------|
| runtime:设备枚举 + memcpy | ✅ 识别 1 个设备,Passed |
| add | ✅ 全部 dtype(f32/f16/bf16)通过 |
| argmax | ✅ 通过 |
| embedding | ✅ 通过 |
| linear | ✅ 通过 |
| rms_norm | ✅ 通过 |
| rope | ✅ 通过(角度计算改用 float32)|
| self_attention | ✅ 通过 |
| swiglu | ✅ 通过 |
| 端到端推理(DeepSeek-R1-Distill-Qwen-1.5B) | ✅ 生成文本正确,约 37.46 tokens/sec |

端到端推理输出示例(prompt “Hello, who are you?”):
```
Generated text: <|User|>Hello, who are you?<|Assistant|><think>
I'm DeepSeek-R1, an AI assistant created exclusively by the Chinese Company DeepSeek. I specialize in helping you tackle complex STEM challenges through analytical thinking,
```

### 3.2 NVIDIA 平台状态
NVIDIA 后端为本次新增天数后端所复用的基线,此前已实现并验证通过(runtime / 8 个算子 / 端到端推理)。

## 四、天数平台适配要点(与标准 CUDA 的差异)

1. **`cublasGemmEx` 签名差异**:CoreX 移植版第 18 个参数 `computeType` 类型为 `cudaDataType`(标准 CUDA 为 `cublasComputeType_t`),须传 `CUDA_R_32F` 而非 `CUBLAS_COMPUTE_32F`。
2. **无 float64(double)支持**:ivcore 设备端 double 数学不可用(torch 明确警告 `Limited support for torch.double`),rope 的角度计算改用 `float`。
3. **缺 `libcudadevrt.so`**:xmake CUDA 规则默认链接它,CoreX 不提供,用软链指向 `libcudart.so` 规避。

## 五、限制说明

- `test/test_infer.py --test` 的逐 token 对齐模式需同时加载 HF 参考模型,HF 模型在 CoreX 上因缺失 float64 GPU 支持而无法工作(平台限制,非本项目代码问题)。天数平台的端到端验证使用 `test_qwen2_load.py --device suda`(纯 LLAISYS 后端)完成。
- 两个平台通过独立的 `xmake f --nv-gpu=y` / `--suda-gpu=y` 开关互不干扰,可分别编译。
1 change: 1 addition & 0 deletions include/llaisys.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ typedef enum {
LLAISYS_DEVICE_CPU = 0,
//// TODO: Add more device types here. Numbers need to be consecutive.
LLAISYS_DEVICE_NVIDIA = 1,
LLAISYS_DEVICE_SUDA = 2,
LLAISYS_DEVICE_TYPE_COUNT
} llaisysDeviceType_t;

Expand Down
39 changes: 39 additions & 0 deletions include/llaisys/qwen2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#ifndef LLAISYS_QWEN2_H
#define LLAISYS_QWEN2_H

#include "../llaisys.h"
#include "tensor.h"

__C {
// Opaque handle for Qwen2 model
typedef struct LlaisysQwen2Model *llaisysQwen2Model_t;

// Create a Qwen2 model
__export llaisysQwen2Model_t qwen2Create(
llaisysDeviceType_t device_type,
int device_id);

// Destroy a Qwen2 model
__export void qwen2Destroy(
llaisysQwen2Model_t model);

// Load a weight tensor into the model by parameter name
__export void qwen2LoadWeight(
llaisysQwen2Model_t model,
const char *name,
llaisysTensor_t weight);

// Forward pass: given input token IDs, produce logits
// input_ids: [seq_len] int64 tensor on CPU
// output_logits: [vocab_size] float32 tensor on CPU (pre-allocated)
__export void qwen2Forward(
llaisysQwen2Model_t model,
llaisysTensor_t input_ids,
llaisysTensor_t output_logits);

// Reset KV cache (for new generation)
__export void qwen2ResetKV(
llaisysQwen2Model_t model);
}

#endif // LLAISYS_QWEN2_H
2 changes: 2 additions & 0 deletions python/llaisys/libllaisys/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .tensor import llaisysTensor_t
from .tensor import load_tensor
from .ops import load_ops
from .qwen2 import load_qwen2


def load_shared_library():
Expand All @@ -38,6 +39,7 @@ def load_shared_library():
load_runtime(LIB_LLAISYS)
load_tensor(LIB_LLAISYS)
load_ops(LIB_LLAISYS)
load_qwen2(LIB_LLAISYS)


__all__ = [
Expand Down
3 changes: 2 additions & 1 deletion python/llaisys/libllaisys/llaisys_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
class DeviceType(IntEnum):
CPU = 0
NVIDIA = 1
COUNT = 2
SUDA = 2
COUNT = 3


llaisysDeviceType_t = ctypes.c_int
Expand Down
36 changes: 36 additions & 0 deletions python/llaisys/libllaisys/qwen2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from ctypes import c_void_p, c_int, c_char_p
from .tensor import llaisysTensor_t
from .llaisys_types import llaisysDeviceType_t

# Handle type for Qwen2 model
llaisysQwen2Model_t = c_void_p


def load_qwen2(lib):
# qwen2Create
lib.qwen2Create.argtypes = [llaisysDeviceType_t, c_int]
lib.qwen2Create.restype = llaisysQwen2Model_t

# qwen2Destroy
lib.qwen2Destroy.argtypes = [llaisysQwen2Model_t]
lib.qwen2Destroy.restype = None

# qwen2LoadWeight
lib.qwen2LoadWeight.argtypes = [
llaisysQwen2Model_t,
c_char_p,
llaisysTensor_t,
]
lib.qwen2LoadWeight.restype = None

# qwen2Forward
lib.qwen2Forward.argtypes = [
llaisysQwen2Model_t,
llaisysTensor_t,
llaisysTensor_t,
]
lib.qwen2Forward.restype = None

# qwen2ResetKV
lib.qwen2ResetKV.argtypes = [llaisysQwen2Model_t]
lib.qwen2ResetKV.restype = None
Loading