Skip to main content

read-onnx

Navigate and understand the LuisaML ONNX runtime (include/onnx, src/onnx). Use when: (1) user asks about ONNX model loading, execution, or kernel generation in LuisaML, (2) user wants to add/modify an ONNX operator, (3) user asks about safetensors/weight-buffer integration, (4) user needs to trace how ONNX JSON becomes a LuisaCompute GPU kernel, (5) user references files under include/onnx or src/onnx.

跳到安装

来源信息

仓库
LuisaGroup/LuisaML
最近来源活动
2026年7月17日 00:26
检测到的 SKILL.md 语言
英语
星标
6
分支
1

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
read_onnx
description
Navigate and understand the LuisaML ONNX runtime (include/onnx, src/onnx). Use when: (1) user asks about ONNX model loading, execution, or kernel generation in LuisaML, (2) user wants to add/modify an ONNX operator, (3) user asks about safetensors/weight-buffer integration, (4) user needs to trace how ONNX JSON becomes a LuisaCompute GPU kernel, (5) user references files under include/onnx or src/onnx.
# Read ONNX (LuisaML) Guide for navigating the LuisaML ONNX embedded-DSL runtime. ## Architecture at a Glance ``` ONNX JSON ──► Model::load_from_json() ──► Graph + Nodes + Variables │ NetworkInstance │ Kernel1D/Kernel2D lambda wrapping net.forward() │ LuisaCompute AST ──► GPU Shader ``` Key design: **C++ runtime builds DSL AST**, not offline source translation. Calls like `Var<T>`, `$if`, `dynamic_range`, `ByteBuffer::read` inside `Operator::forward()` generate AST nodes. When wrapped in a `Kernel1D` lambda, the whole graph becomes a GPU kernel. ## Directory Map | Path | Role | |---|---| | `include/onnx/onnx.h` | Data layer: `Model`, `Graph`, `Node`, `Variable`, `Attribute`, `DataType` | | `include/onnx/operator.h` | Base class `Operator`, registration macros | | `include/onnx/tensor.h` | `ITensor`, `Tensor<T>`, `ConstTensor<T>` | | `include/onnx/tensor_table.h` | `TensorTable` / `TensorEntry`: owning + borrowing storage | | `include/onnx/network_instance.h` | `NetworkInstance`, `PreparedGraph`: execution engine | | `include/onnx/operators/common.h` | Type dispatch, vectorized read/write, broadcast, shape utils | | `include/onnx/dynamic_array/` | `DynamicArray<T>` backends: Local/Buffer/View/Scalar/Linear/FP4/FP8 | | `include/onnx/register_allocator/` | Register allocator (graph-coloring memory reuse) | | `src/onnx/onnx.cpp` | JSON parsing (`yyjson`) | | `src/onnx/network_instance.cpp` | Tensor creation, lifetime analysis, operator dispatch | | `src/onnx/operators/*.cpp` | Per-operator DSL implementations | ## Core Classes ### Model / Graph / Node / Variable (`onnx.h`) - **Model**: top-level; entry `Model::load_from_json(json_str)`. - **Graph**: holds `variables`, ordered `nodes`, `input`/`output` refs. Supports `set_parent` for subgraphs (e.g. `If`). - **Node**: `op_type`, `inputs`, `outputs`, `attributes`. - **Variable**: name, `DataType`, shape, and either: - `raw_data` (base64 CPU bytes), or - `data_offsets` → `buffer_start/buffer_end` into an external `ByteBuffer`. - **Attribute**: `luisa::variant` of float/int/string/graph/floats/ints/strings/graphs. ### Operator (`operator.h`) Pure virtual interface: ```cpp virtual void forward(luisa::span<std::reference_wrapper<ITensor>> inputs, luisa::span<std::reference_wrapper<ITensor>> outputs) = 0; ``` Optional hooks: - `is_output_view()` / `can_operate_inplace()` / `need_outline()` - `set_environment(NetworkInstance&)` Registration macro: ```cpp REGISTER_TO_DEFAULT_OPSET(Gemm) { // read node.attributes... return luisa::make_unique<Gemm>(...); } ``` ### Tensor (`tensor.h`) - `ITensor`: shape, stride, element type, view/const flags. - `Tensor<T, Container>`: default container `DynamicArray<T>`. - Multidim `operator()` supports both host constants and DSL `Var<uint>` indices. ### DynamicArray (`dynamic_array/`) Variant container bridging tensor to LuisaCompute storage: | Mode | DSL behavior | |---|---| | `LocalData<T>` | `Local<T>` AST node | | `BufferData<T>` | `byte_buffer->read<T>(offset)` / `write(offset, val)` | | `ViewData<T>` | offset into another `LocalData` | | `ScalarData<T>` | zero storage; all indices return same constant | | `LinearData<T>` | zero storage; value = `start + idx * delta` | | `FP4Data<T>` / `FP8Data<T>` | quantized ByteBuffer with packed offset logic | ## Execution Flow ### 1. Load ```cpp auto model = Model::load_from_json(json_string); ``` - `yyjson` parses JSON. - `Graph::mark_constants()` flags variables never produced by a node. ### 2. Configure NetworkInstance ```cpp NetworkInstance net; net.set_model(std::move(model)); net.set_input("input", input_tensor); net.set_output("output", output_tensor); net.set_weight_buffer(weight_byte_buffer); // safetensors blob as ByteBuffer net.set_warp_size(32); ``` ### 3. Forward (happens at DSL capture time, not CPU runtime) Inside `NetworkInstance::forward_graph()`: **Phase 0** — Create operators from `OperatorSet`. **Phase 1** — `build_last_use_map()`: record last node index each variable is used as input; extend lifetimes for subgraphs. **Phase 2** — `create_intermediate_tensors_pooled()`: register allocator builds interference graph, colors it, assigns `PhantomStorage` slots. Intermediate tensors become `DynamicArray::ViewData` into slots. **Phase 3** — `execute_operators()`: ```cpp op->set_environment(*this, tensor_table); $outline_with_name(op->get_name()) { op->forward(op_inputs, op_outputs); }; ``` Each `forward()` issues DSL constructs (`dynamic_range`, `Var<T>`, `$if`, etc.). ### 4. Compile & Run ```cpp Kernel1D kernel = [&] { net.forward(); }; auto shader = device.compile(kernel); stream << shader().dispatch(1) << synchronize(); ``` ## Weight Input (safetensors) The runtime **does not parse .safetensors directly**. Instead: 1. Caller parses `.safetensors` (Python or C++), sorts by name, concatenates bytes into a blob. 2. Upload blob as LuisaCompute `Buffer<uint8_t>` / `ByteBuffer` → `Var<ByteBuffer>`. 3. `NetworkInstance::set_weight_buffer(byte_buffer_var)`. 4. ONNX JSON initializers use `data_offsets: [start, end]` instead of `raw_data`. At tensor creation (`network_instance.cpp`): ```cpp if (var.is_trainable_weight()) { auto [buf_start, buf_end] = var.get_buffer_range(); tensor = luisa::make_unique<NNTensor<T>>( shape, typename NNTensor<T>::container_type{ num_elements, weight_buffer_, buf_start}); } ``` This binds a `BufferData<T>` backed by the shared `ByteBuffer`. For small embedded weights, `create_tensor_for_var` detects: - **All-equal** → `ScalarData` - **Arithmetic progression** → `LinearData` - **General** → `LocalData` or `BufferData` ## Adding an Operator 1. Create `src/onnx/operators/<op_name>.cpp`. 2. Inherit `Operator`, implement `forward()` using DSL (`Var<T>`, `dynamic_range`, `$if`). 3. Use utilities from `include/onnx/operators/common.h` for type dispatch, vectorized loads, broadcast indexing. 4. Register: ```cpp REGISTER_TO_DEFAULT_OPSET(MyOp) { // parse node.attributes... return luisa::make_unique<MyOp>(...); } ``` 5. Include new cpp in build system. ## Key Optimization Paths When reading operator implementations, look for these DSL-level optimizations: - **CooperativeVector**: `use_coop_vec_`, `CoopVector<T>`, `cooperative_mat_mul_add` - **Warp vectorization**: `warp_size_`, `warp_active_sum`, `float4`/`half4` chunking - **Normal vectorization**: `float4`/`half4` reads from `BufferData` when memory contiguous - **Scalar fallback**: plain `fma` loops - **Constant compression**: `ScalarData`, `LinearData` - **Register allocation**: intermediate tensors pooled via graph coloring in `register_allocator/` ## Quick Reference: File → Question | Question | Go to | |---|---| | How is ONNX JSON parsed? | `src/onnx/onnx.cpp` | | How are tensors created and pooled? | `src/onnx/network_instance.cpp` | | How does a specific op work? | `src/onnx/operators/<op>.cpp` | | How do I add a new op? | `include/onnx/operator.h` + any `src/onnx/operators/*.cpp` | | How are weights bound to ByteBuffer? | `src/onnx/network_instance.cpp` (`create_tensor_for_var`) | | How is memory reused between intermediates? | `include/onnx/register_allocator/` + `network_instance.cpp` | | What DSL types/backends exist? | `include/onnx/dynamic_array/` |
在 GitHub 查看