Skip to content

Commit b83906c

Browse files
Add MLAS and ONNX Runtime pages from benchmarks repo.
Document int4 MatMulNBits IME path on X60: 9–10× from accuracy_level=4, isolated MLAS kernel rates, nav and cross-links from homepage and board pages. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent ab2e8c3 commit b83906c

7 files changed

Lines changed: 128 additions & 7 deletions

File tree

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ Each RISC-V board exposes several compute paths. We benchmark and tune them inde
1414
| ---- | ---------- | ---------------------- |
1515
| **Scalar** | Scalar ISA and portable C kernels — the correctness baseline | `rv64gc` on VisionFive 2; `OPENBLAS_CORETYPE=RISCV64_GENERIC`; U74 **4×4 DGEMM** tuning |
1616
| **Vector** | ISA vector extensions (RVV) in shared libs | OpenBLAS `RISCV64_ZVL256B` on SpaceMiT X60 (VLEN=256); `ZVL128B` where VLEN=128; the `gemv_n` bug we fixed |
17-
| **Specific** | Silicon-specific custom units beyond RVV | X60 **IME** (`smt.vmadot`) int8 on [RV2](boards/RV2.html) / [F3](boards/F3.html) |
17+
| **Specific** | Silicon-specific custom units beyond RVV | X60 **IME** (`smt.vmadot`) int8 on [RV2](boards/RV2.html) / [F3](boards/F3.html); [ONNX Runtime](apps/onnx.html) int4 via MLAS |
1818
| **GPU** | Discrete or integrated accelerators | On the roadmap — not yet in our RISC-V board benchmarks |
1919

20-
Recent highlights on the Orange Pi RV2 (SpaceMiT X60, RVV): fixing an OpenBLAS `gemv_n` bug restores correctness across [BLAS](scientific-libs/blas.html), [LAPACK](scientific-libs/lapack.html), [ELPA](scientific-libs/elpa.html), [HPL](apps/hpl.html), and [Quantum ESPRESSO](apps/qe.html) — with patched RVV reaching **10.53 GFLOP/s** on Linpack, **1.58×** on a dense eigensolve, and **1.31×** on a 64-atom Si DFT SCF.
20+
Recent highlights on the Orange Pi RV2 (SpaceMiT X60, RVV): fixing an OpenBLAS `gemv_n` bug restores correctness across [BLAS](scientific-libs/blas.html), [LAPACK](scientific-libs/lapack.html), [ELPA](scientific-libs/elpa.html), [HPL](apps/hpl.html), and [Quantum ESPRESSO](apps/qe.html) — with patched RVV reaching **10.53 GFLOP/s** on Linpack, **1.58×** on a dense eigensolve, and **1.31×** on a 64-atom Si DFT SCF. On the IME path, setting `accuracy_level=4` on ONNX `MatMulNBits` nodes unlocks **9–10×** int4 LLM decode vs the fp32 fallback — see [ONNX Runtime](apps/onnx.html) and [MLAS](scientific-libs/mlas.html).
2121

2222
## Scientific libs
2323

@@ -28,13 +28,15 @@ Library-level probes — performance *and* numerical correctness:
2828
- **[NumPy](scientific-libs/numpy.html)**`bench_blas.py` DGEMM and `eigvalsh` through the SciPy stack
2929
- **[LAPACK](scientific-libs/lapack.html)** — LAPACK path via NumPy `eigvalsh`
3030
- **[ELPA](scientific-libs/elpa.html)** — dense eigensolver (CP2K / VASP class workloads)
31+
- **[MLAS](scientific-libs/mlas.html)** — ONNX Runtime QNBit int4 GEMM; isolated IME kernel rates on X60
3132

3233
## Apps
3334

3435
End-to-end application benchmarks on the same boards and EESSI toolchain:
3536

3637
- **[HPL](apps/hpl.html)** — High Performance Linpack; cross-board summary and A/B configs from [opensolvers/benchmarks](https://github.com/opensolvers/benchmarks)
3738
- **[Quantum ESPRESSO](apps/qe.html)** — plane-wave DFT SCF (`pw.x`); whole-application BLAS backend A/B with per-routine timers
39+
- **[ONNX Runtime](apps/onnx.html)** — int4 `MatMulNBits` LLM decode; `accuracy_level=4` unlocks X60 IME (**9–10×**)
3840

3941
## Boards
4042

_config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ navigation_apps:
1919
url: /apps/hpl.html
2020
- title: Quantum ESPRESSO
2121
url: /apps/qe.html
22+
- title: ONNX Runtime
23+
url: /apps/onnx.html
2224

2325
navigation_scientific_libs:
2426
- title: BLAS
@@ -31,3 +33,5 @@ navigation_scientific_libs:
3133
url: /scientific-libs/lapack.html
3234
- title: ELPA
3335
url: /scientific-libs/elpa.html
36+
- title: MLAS
37+
url: /scientific-libs/mlas.html

apps/onnx.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# ONNX Runtime
2+
3+
[ONNX Runtime](https://onnxruntime.ai/) int4 **`MatMulNBits`** inference on SpaceMiT X60 — a whole-graph LLM decode probe through ORT's MLAS `SQNBit` path and the X60 **IME** (`smt.vmadot`).
4+
5+
Benchmark source: [opensolvers/benchmarks/onnx](https://github.com/opensolvers/benchmarks/tree/main/onnx) — Llama-7B-proportioned FFN stack, `patch_accuracy_level.py`, and `onnxruntime_perf_test` runners.
6+
7+
Library-level kernel numbers: [MLAS](../scientific-libs/mlas.html). Raw IME microbenchmarks: [Orange Pi RV2](../boards/RV2.html#ime-integer-matrix-extension).
8+
9+
## The workload
10+
11+
8-layer stack of FFN/projection blocks: `hidden=4096`, `ffn=11008`, **16 `MatMulNBits` nodes** (com.microsoft), 4-bit symmetric weights, `block_size=32` — the big quantised GEMMs in LLM decode. Measured with `onnxruntime_perf_test` (`-m times`, `-r 8`) at decode shape **M=1**.
12+
13+
## Before / after — one missing attribute
14+
15+
ORT selects the int4 compute variant in `matmul_nbits.cc`:
16+
17+
| `accuracy_level` | MLAS path | X60 backend |
18+
| ---------------- | --------- | ----------- |
19+
| **4** | `SQNBIT_CompInt8` | IME `smt.vmadot` kernel |
20+
| anything else | `SQNBIT_CompFp32` | *no kernel registered* → generic fp32 dequant + SGEMM fallback |
21+
22+
The generated model had **zero** `accuracy_level` attributes across all 16 nodes. Setting `accuracy_level=4` at generation time (or via [`patch_accuracy_level.py`](https://github.com/opensolvers/benchmarks/blob/main/onnx/patch_accuracy_level.py)) selects the IME path — **no kernel code change**.
23+
24+
### End-to-end latency (Orange Pi RV2 / X60)
25+
26+
| Configuration | Before (CompFp32 fallback) | After (IME CompInt8) | Speedup |
27+
| ------------- | -------------------------: | -------------------: | ------: |
28+
| Single thread (`-x1`) | 31,956 ms | **3,522 ms** | **9.1×** |
29+
| 8 threads (`-x8`) | 6,074 ms | **590 ms** | **10.3×** |
30+
31+
- Peak RSS: ~1023 MB → **842 MB** (no fp32 dequant buffers).
32+
- x1→x8 scaling on the fixed path: **~** across 8 cores.
33+
34+
## How it was confirmed
35+
36+
- **Probe:** `fprintf` in both IME kernel paths — at M=1, **neither fired** on the broken model; the kernel never ran.
37+
- **Roofline:** X60 STREAM triad = 3.85 GB/s (1T) / ~10.6 GB/s (8T); int4 B traffic ≈ 361 MB/inference → bandwidth floor ~0.09 s (x1). Measured latency was **178–341× above** the floor — wrong code path, not slow kernel.
38+
- **Bytes:** `grep -c accuracy_level model.onnx` → 0 across 16 nodes.
39+
40+
After the fix, the probe fires and first inference drops from ~32 s to ~4.5 s.
41+
42+
## Kernel micro-opt was a regression
43+
44+
The original goal was an M&lt;4 RVV gemv fast path inside the IME `SQ4BitGemmKernel_CompInt8`. Once the correct path executed, fair A/B at M=1 showed:
45+
46+
| Kernel (M=1) | Latency |
47+
| ------------ | ------: |
48+
| Hand-written RVV fast path | 4,521 ms |
49+
| Stock IME `smt.vmadot` tile | **3,522 ms** |
50+
51+
The bespoke path was **28% slower** and was reverted. **Config beat kernels** — one attribute gave 9–10×; the hand kernel gave −28%.
52+
53+
## Reproduce
54+
55+
```sh
56+
python3 patch_accuracy_level.py int4_ffn.onnx int4_ffn_acc4.onnx
57+
58+
onnxruntime_perf_test -e cpu -I -m times -r 8 -x 1 int4_ffn_acc4.onnx
59+
onnxruntime_perf_test -e cpu -I -m times -r 8 -x 8 int4_ffn_acc4.onnx
60+
```
61+
62+
**Toolchain:** ONNX Runtime 1.29.0, `foss/2025b`, X60 `smt.vmadot` MLAS backend, `-march=rv64gcv_zvl256b_zfh_zvfh`.
63+
64+
## Takeaways
65+
66+
1. **Prove the code runs before optimising it** — a one-shot probe would have saved the kernel-tuning detour.
67+
2. **Roofline early** — traffic math pointed at "wrong path" in minutes.
68+
3. **Comments aren't evidence** — grep the artifact, not the intent.
69+
4. **Config beats kernels**`accuracy_level=4` → 9–10×; hand-tuned RVV → −28%.

boards/F3.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Cross-board confirmation from [opensolvers/benchmarks](https://github.com/openso
88

99
Same K1 / X60 silicon as the Orange Pi RV2 — each chip has SpaceMiT's **IME** int8 matrix unit (`smt.vmadot`) on **cluster 0** (cores 0–3, **512 KB shared L2**). The IME is separate from RVV: one instruction accumulates a **4×4 int32** tile from **4×8 int8** operands.
1010

11-
Detailed `ime-bench` numbers were measured on the [RV2](RV2.html) (peak **~42 GOP/s** int8 GEMM vs ~5 GOP/s RVV int8, **~7–8×**). The BPI-F3 has the same IME hardware; pin to core 0 when running [benchmarks/ime](https://github.com/opensolvers/benchmarks/tree/main/ime). Useful for int8 / quantised inference stacks (e.g. llama.cpp's `GGML_CPU_RISCV64_SPACEMIT` backend).
11+
Detailed `ime-bench` numbers were measured on the [RV2](RV2.html) (peak **~42 GOP/s** int8 GEMM vs ~5 GOP/s RVV int8, **~7–8×**). The BPI-F3 has the same IME hardware; pin to core 0 when running [benchmarks/ime](https://github.com/opensolvers/benchmarks/tree/main/ime). End-to-end int4 inference: [ONNX Runtime](../apps/onnx.html) and [MLAS](../scientific-libs/mlas.html).
1212

1313
## HPL
1414

boards/RV2.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Microbenchmarks in [opensolvers/benchmarks/ime](https://github.com/opensolvers/b
1616
| 768×768×512 | ~5.2 GOP/s | **42 GOP/s** (peak) | **8.1×** |
1717
| 1024×1024×512 | 5.2 GOP/s | **32 GOP/s** | 6.2× |
1818

19-
Peak **~42 GOP/s** single-core — vs ~5 GOP/s for a straightforward RVV int8 path. llama.cpp ships an X60 IME backend (`GGML_CPU_RISCV64_SPACEMIT`) for block-scaled Q4 inference; see also [papers/x60-ime-block-scale-optimization](https://github.com/opensolvers/benchmarks/blob/main/papers/x60-ime-block-scale-optimization.md) in the benchmarks repo.
19+
Peak **~42 GOP/s** single-core — vs ~5 GOP/s for a straightforward RVV int8 path. End-to-end int4 LLM decode through [ONNX Runtime](../apps/onnx.html) and isolated [MLAS](../scientific-libs/mlas.html) kernel rates use the same IME hardware; see also [papers/x60-ime-block-scale-optimization](https://github.com/opensolvers/benchmarks/blob/main/papers/x60-ime-block-scale-optimization.md) in the benchmarks repo.
2020

2121
## HPL via EESSI
2222

scientific-libs/mlas.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# MLAS
2+
3+
[MLAS](https://github.com/microsoft/onnxruntime/tree/main/onnxruntime/core/mlas) (Microsoft Linear Algebra Subprograms) is ONNX Runtime's in-tree linear-algebra layer. On RISC-V SpaceMiT X60 it exposes an **IME** (`smt.vmadot`) backend for int4 **QNBit** GEMM — the kernel behind ONNX Runtime's `MatMulNBits` nodes.
4+
5+
Benchmark source: [opensolvers/benchmarks/onnx](https://github.com/opensolvers/benchmarks/tree/main/onnx)[`bench_qnbit_mlas.cpp`](https://github.com/opensolvers/benchmarks/blob/main/onnx/bench_qnbit_mlas.cpp) links directly against `libonnxruntime_mlas.a` and calls the same entry points as ORT's `matmul_nbits.cc`.
6+
7+
See also the [ONNX Runtime app](../apps/onnx.html) for end-to-end int4 LLM-FFN inference and the [IME microbenchmarks](../boards/RV2.html#ime-integer-matrix-extension) on the Orange Pi RV2.
8+
9+
## What it probes
10+
11+
[`../ime`](https://github.com/opensolvers/benchmarks/tree/main/ime) benchmarks raw `smt.vmadot` int8 GEMM. MLAS sits one layer up: **4-bit symmetric weights**, `BlkLen=32`, external per-block scales — the packing and compute path ORT uses for quantised LLM weights.
12+
13+
The harness calls:
14+
15+
- `MlasQNBitGemmPackQuantBData` — pack quantised B (nbits=4, no zero-point),
16+
- `MlasQNBitGemmBatch<float>` — run the GEMM,
17+
18+
with `thread_pool=nullptr` so results are **single-thread kernel rates**, not ORT graph throughput.
19+
20+
## Single-thread kernel rates (X60, one core)
21+
22+
| Shape (M×K×N) | Kernel rate | packed B |
23+
| ------------- | ----------: | -------: |
24+
| 1 × 4096 × 11008 | **0.47 GOP/s** | 22.5 MB |
25+
| 1 × 11008 × 4096 | **0.39 GOP/s** | 22.5 MB |
26+
| 1 × 4096 × 4096 | **0.48 GOP/s** | 8.4 MB |
27+
28+
These isolate kernel efficiency. Production [ONNX](../apps/onnx.html) inference fans the same kernel across 8 cores (~**** scaling at M=1 decode).
29+
30+
## RISC-V pack gotcha
31+
32+
The X60 IME backend registers only the plain `SQ4BitGemmPackQuantBData` (`memcpy`), not the x64-style `...AndBlkSum` variant. Scales stay external and are passed at compute time via `QuantBScale`. A second "finalize" pack call with `QuantBData=nullptr` (x64 recipe) `memcpy`s from `NULL` and segfaults — the harness does a single data-only pack.
33+
34+
## Kernel tuning note
35+
36+
Once the correct ORT compute path was confirmed (`accuracy_level=4``SQNBIT_CompInt8`), a hand-written RVV gemv "fast path" inside the IME kernel was **28% slower** than the stock `smt.vmadot` tile path at M=1 and was reverted. Config beat micro-optimisation — see [ONNX app](../apps/onnx.html).
37+
38+
## Reproduce
39+
40+
```sh
41+
make board CXX=$GCC14/bin/g++
42+
LD_LIBRARY_PATH=$GCC14/lib64:$LD_LIBRARY_PATH ./qnbit-mlas-bench 1 4096 11008 50
43+
```
44+
45+
**Toolchain:** ONNX Runtime 1.29.0, `foss/2025b`, X60 `smt.vmadot` (XsmtVdot v1.0), `-march=rv64gcv_zvl256b_zfh_zvfh`.

website.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ Context file for structural and content decisions on [opensolvers.com](https://w
1919

2020
1. **Home**
2121
2. **Boards** — VisionFive 2, OrangePi RV2, BananaPi F3
22-
3. **Apps** — HPL, Quantum ESPRESSO
23-
4. **Scientific libs** — BLAS, DGEMM, NumPy, LAPACK, ELPA
22+
3. **Apps** — HPL, Quantum ESPRESSO, ONNX Runtime
23+
4. **Scientific libs** — BLAS, DGEMM, NumPy, LAPACK, ELPA, MLAS
2424

2525
Nav config: `_config.yml` (`navigation`, `navigation_boards`, `navigation_apps`, `navigation_scientific_libs`). Rendered in `_includes/header.html`. Cayman theme requires `_layouts/default.html` override to include the header.
2626

2727
## Content sources
2828

2929
| Source | Used for |
3030
|--------|----------|
31-
| [opensolvers/benchmarks](https://github.com/opensolvers/benchmarks) | BLAS verification (`dgemm/`), HPL configs (`hpl/`), ELPA (`elpa/`), QE (`qe/`), NumPy/LAPACK (`numpy/`) |
31+
| [opensolvers/benchmarks](https://github.com/opensolvers/benchmarks) | BLAS (`dgemm/`), HPL (`hpl/`), ELPA (`elpa/`), QE (`qe/`), NumPy/LAPACK (`numpy/`), ONNX/MLAS (`onnx/`) |
3232
| [EESSI/docs#818](https://github.com/EESSI/docs/pull/818) | VisionFive 2 / U74 OpenBLAS + HPL |
3333
| [EESSI/docs#819](https://github.com/EESSI/docs/pull/819) | Orange Pi RV2 / X60 RVV `gemv_n` fix + HPL |
3434
| [easyconfigs#26436](https://github.com/easybuilders/easybuild-easyconfigs/pull/26436) | U74 OpenBLAS package |
@@ -44,6 +44,7 @@ Nav config: `_config.yml` (`navigation`, `navigation_boards`, `navigation_apps`,
4444

4545
| Date | Decision |
4646
|------|----------|
47+
| 2026-07-17 | Add MLAS (lib) and ONNX Runtime (app) pages from `benchmarks/onnx` |
4748
| 2026-07-15 | Vector box: add ZVL256B and ZVL128B subtitles |
4849
| 2026-07-15 | Compute-backends diagram: RV64GC / IME subtitles; U74 DGEMM under Scalar |
4950
| 2026-07-15 | Homepage: compute-backends illustration (Scalar / Vector / Specific / GPU) |

0 commit comments

Comments
 (0)