Skip to content

RISC-V: Add TRSM RVV Kernels for ZVL Targets#5895

Merged
martin-frbg merged 6 commits into
OpenMathLib:developfrom
moluopro:develop
Jul 8, 2026
Merged

RISC-V: Add TRSM RVV Kernels for ZVL Targets#5895
martin-frbg merged 6 commits into
OpenMathLib:developfrom
moluopro:develop

Conversation

@moluopro

@moluopro moluopro commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Use ZVL-specific TRSM RVV kernels instead of reusing the x280 legacy path:

Precision LN LT RN RT
S RVV RVV RVV RVV
D generic generic RVV RVV
C generic generic RVV RVV
Z generic generic generic RVV
  • RN/RT are the most natural RVV paths because they update contiguous rows of C.
  • S keeps RVV for all four cases because the measured gains are stable.
  • D/C LN/LT and Z LN/LT/RN remain generic because forced RVV provided little, negative, or inconsistent benefit.

Risk of Direct x280 Reuse

Directly changing STRSM/DTRSM/CTRSM/ZTRSMKERNEL_{LN,LT,RN,RT} to trsm_kernel_*_rvv_v1.c, while also enabling the existing x280 TRSMCOPY*_rvv_v1.c / ZTRSMCOPY*_rvv_v1.c copy overrides, was tested and found to be incorrect.

ztrsm failed: side=142 uplo=121 trans=111 diag=131 m=7 n=8 at (0,4)
got=(-0.0051051559713729162,0.016862573076553602)
want=(-0.00056447459744372863,0.01384110379083936)

This corresponds to Right/Upper/NoTrans/NonUnit with a small m=7, n=8 case. After fixing only the right-side tail handling, the failure moved to the left-side path:

strsm failed: side=141 uplo=121 trans=112 diag=131 m=31 n=17 at (16,0)
got=(-0.13186925649642944,0)
want=(-0.11499714831632926,0)

This corresponds to Left/Upper/Trans/NonUnit. The root cause is not a single numerical error. The x280 kernels use RVV instructions, but they are still tied to the x280 block sizes, copy/packed layout, and GEMM unroll settings. These assumptions differ for the ZVL targets, so direct reuse of x280 breaks correctness. The same layout and block-size constraints also apply to the complex paths that dispatch through GEMM_KERNEL_L/R.

Build Configuration

make TARGET=RISCV64_ZVL256B BINARY=64 ARCH=riscv64 \
     CC=gcc HOSTCC=gcc NOFORTRAN=1 NO_LAPACK=1 NUM_THREADS=8 clean
make -j8 TARGET=RISCV64_ZVL256B BINARY=64 ARCH=riscv64 \
     CC=gcc HOSTCC=gcc NOFORTRAN=1 NO_LAPACK=1 NUM_THREADS=8 shared
make -j8 TARGET=RISCV64_ZVL256B BINARY=64 ARCH=riscv64 \
     CC=gcc HOSTCC=gcc NOFORTRAN=1 NO_LAPACK=1 NUM_THREADS=8 shared
make -C benchmark -j8 TARGET=RISCV64_ZVL256B BINARY=64 ARCH=riscv64 \
     CC=gcc HOSTCC=gcc NOFORTRAN=1 NO_LAPACK=1 NUM_THREADS=8 \
     strsm.goto dtrsm.goto ctrsm.goto ztrsm.goto

Correctness

Additional standalone correctness coverage:

Dimension Coverage
Functions strsm, dtrsm, ctrsm, ztrsm
side Left, Right
uplo Upper, Lower
trans real: NoTrans, Trans; complex: NoTrans, Trans, ConjTrans
diag Unit, NonUnit
alpha zero and non-zero
sizes includes 0, 1, 2, 4, 7, 16, 31, 64, 129 and other non-block-aligned sizes

Passed: zvl_trsm_check: all 1872 cases passed

Performance

The benchmark uses .goto programs generated from OpenBLAS benchmark/trsm.c, fixed to:

OPENBLAS_NUM_THREADS=1
OPENBLAS_SIDE={L|R}
OPENBLAS_UPLO=U
OPENBLAS_TRANS={N|T}
OPENBLAS_DIAG=N

Sizes and loop counts:

n OPENBLAS_LOOPS
128 50
512 10
1024 3

End-to-end TRSM time includes triangular block solves, local updates, GEMM updates, packing, and call overhead. trsm_kernel_*_rvv.c mainly optimizes the small triangular block solve and local updates, so even small cases are diluted by work outside the TRSM kernel. As size grows, the GEMM-update share usually increases, making the dilution more visible.

Test hardware: Spacemit X60. Results are in MFlops; higher is better.

Function Case n Generic RVV RVV Speedup
strsm LN 128 2308.50 3530.56 +52.94%
strsm LN 512 4426.26 5127.40 +15.84%
strsm LN 1024 4810.65 5250.21 +9.14%
strsm LT 128 2283.05 3533.92 +54.79%
strsm LT 512 4370.54 4916.41 +12.49%
strsm LT 1024 4711.67 5113.93 +8.54%
strsm RN 128 3381.70 5699.36 +68.54%
strsm RN 512 5117.10 6360.43 +24.30%
strsm RN 1024 5037.78 5581.60 +10.79%
strsm RT 128 3154.14 5120.69 +62.35%
strsm RT 512 5153.16 6349.95 +23.22%
strsm RT 1024 4953.93 5876.72 +18.63%
dtrsm RN 128 2075.90 2775.90 +33.72%
dtrsm RN 512 2161.38 2345.62 +8.52%
dtrsm RN 1024 2340.96 2752.01 +17.56%
dtrsm RT 128 2012.54 2549.03 +26.66%
dtrsm RT 512 2181.79 2317.04 +6.20%
dtrsm RT 1024 2335.42 2752.88 +17.88%
ctrsm RN 128 2759.11 3559.29 +29.00%
ctrsm RN 512 3412.06 3801.67 +11.42%
ctrsm RN 1024 3667.23 3979.75 +8.52%
ctrsm RT 128 2769.86 3453.38 +24.68%
ctrsm RT 512 3555.77 3885.27 +9.27%
ctrsm RT 1024 3691.31 4008.20 +8.58%
ztrsm RT 128 1725.09 1869.54 +8.37%
ztrsm RT 512 1934.12 2019.79 +4.43%
ztrsm RT 1024 2056.39 2068.60 +0.59%

@moluopro

moluopro commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Why did the pipeline finish so quickly this time?
Though, unsurprisingly, one of the QEMU jobs still failed.

@martin-frbg

Copy link
Copy Markdown
Collaborator

There was no backlog of other PR runs, so none stayed queued for ages, I think ? Perhaps the internal retries in our friend the C910V job should be removed too, as there's nothing to be learned from them (and I haven't been able to
reproduce that fork hang on actual hardware)

@moluopro

moluopro commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

After watching bsd-aarch64 crawl for ages, I am starting to understand why you were thinking about removing this job. It really does test one’s patience.

@martin-frbg martin-frbg added this to the 0.3.34 milestone Jul 8, 2026
@martin-frbg martin-frbg merged commit 788d544 into OpenMathLib:develop Jul 8, 2026
104 of 108 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants