待翻譯:RVV Benchmark SiFive P870 (Lanxin LX500, Epic Semi Contrail AIx)
AI 服務暫時不可用,以下為來源摘要,待恢復後補全翻譯:RVV benchmark SiFive P870 The SiFive P870 is a 6-wide out-of-order RVA23 compatible core, with a VLEN of 128. See also the HotChips 2023 presentation. The Epic Semi Contrail AIx server contains 32 RVA23 RISC-V performan…
AI 服務暫時不可用,以下為來源正文,待恢復後補全翻譯。
RVV benchmark SiFive P870 The SiFive P870 is a 6-wide out-of-order RVA23 compatible core, with a VLEN of 128. See also the HotChips 2023 presentation. The Epic Semi Contrail AIx server contains 32 RVA23 RISC-V performance cores and 16 separate RISC-V cores with a larger vector length and matrix extension. It seems to use the Lanxin LX500 SOC (compare picture to Epic Semi slides), of which the 32 performance cores are very likely SiFive P870s, see below. Lanxin advertises a 15.27/GHz SPECint2006 estimate and a frequency of 2.7 GHz. After helping boot the Contrail AIx server at the RISC-V Summit Europe 2026 Epic Semi Booth, we were fortunate to be allowed to run various tests on it. We managed to run rvv-bench, the dav1d/ffmpeg checkasm benchmarks, libvolk/7zip benchmarks and collect a few more general infos about the system. Note that the pre-installed compilers were rather old, though I don't recall the exact version. Also keep in mind that this it was an early development system with the frequency was locked at 2 GHz, presumably the final system will reach the advertised 2.7 GHz. The /proc/cpuinfo mvendorid matched SiFive and the rvv-bench throughput measurements only fit the SiFive-P870 in SiFives portfolio. Though it's unclear whether it's the regular P870 or the P870-D, or if there even is a meaningful difference. Return to parent page Based on this commit. Benchmarks memcpy memset memreverse strlen utf8 count merge escaped lines mandelbrot reverse byte order of 32 bit elements 4-bit lookup table 6-bit lookup table histogram ascii to utf16 (zero extend 1->2 bytes) ascii to utf32 (zero extend 1->4 bytes) chacha20 poly1305 base64 encode 8x8 transpose SEW=8 8x8 transpose SEW=16 veclibm uarch measurement tool Benchmarks Performance observations Strip-mining and maximizing LMUL without spilling is basically optimal. Manual tail handling, or unrolling doesn't improve performance. For small kernels LMUL=4 may be slightly faster than LMUL=8 (see memset and memcpy) Smaller LMUL may be faster for indexed load/store bound kernels, when index collisions are likely to occur with larger LMUL (see LUT4) LMUL=1 memcpy is slightly faster than LMUL>1 memcpy for a short range of mid-sized inputs (0.2-1.5KiB), however it's significantly worse than even LMUL=2 for >10KiB. The core supports Ovlt, that is, execution time depends on vl not LMUL. Weirdly LMUL>1 vmerge.vvm vl=1 ta ma throughput is different from vmerge.vv vl=1 ta mu Ovlt only partially applies to register dependent permutation instructions, see below: LMUL>1 register dependent permutation have a huge constant throughput penalty of about 8 cycles, so LMUL=1/2/4/8 vslideup.vx has a inverse throughput of 1/8/10/14 (vslide1up.vx which has 1/2/4/8) This makes LMUL=2 vrgather/vcompress/vslideup/vslidedown basically unusable. However, because the benchmarks were run on early hardware with limited time, I can't be sure this isn't a configuration problem, or an artifact of the measurement that may not impact realistic code. Still, the LMUL=2 LUT4 results look quite damning. The core implements sub-quadratic LMUL scaling for vrgather.vv and vcompress by only reading from registers that actually need to be read from. This is great for LMUL=4 and LMUL=8, but the ~8 cycle penalty still applies, LMUL=2 vrgather/vcompress are still substantially worse than with the standard quadratic scaling. When a vrgather.vv operation doesn't need to cross register boundaries, multiple LMUL=1 gathers are still significantly faster due to the constant overhead. (see byteswap and LUT4) Segmented load/stores with nf=2/3/4 are fast. (see ASCII to utf16/utf32) Fault-only-first loads are quite fast and can get close to the memory bandwidth. For memory bandwidth-limited problems, it's probably worth using page-aligned unit-stride loads for best performance. (see strlen) Indexed load/stores are as faster than doing the equivalent load/stores in scalar code. (see LUT4 and LUT6) Strided load are slow (see ASCII to utf16/utf32) Strided stores are extremely slow, about 10x worse than the SpacemiT X100 in memreverse Reinterpreting as mask as a vector seems to have no overhead. (see uarch-tool) Masked instruction variants are cheap. (see table below) The above may sound negative, but that's because most things look good/as expected. We've seen great performance improvements from using RVV on the hardware across the board. It is, as of writing (August 2026), the fastest RISC-V silicon I had access to. The only problems are the constant LMUL>1 register dependent permutation overhead and the performance of indexed stores. To help quantify the impact of the permutation overhead: Most code will be able to use unrolled LMUL=1 vrgather.vv and use LMUL=1 or LMUL=4/8 for the other permutations. The most likely impacted code is one that need a 256-bit wide vrgather (for VLEN=128, you should use LMUL=2 to implement it), and code that uses vslideup.vx/vslidedown.vx and happens to be LMUL=2. I would advise developers not to optimize around this hardware problem (if still present in the final hardware), because it doesn't apply to any other RVV hardware and would penalize other implementations. Microarchitecture SiFive has thankfully given us a lot of information about their P870 microarchitecture in their HotChips 2023 presentation. The Chips and Cheese article does a good job in explaning the slides and comparing it to other processor designs. Microarchitecture speculations LMUL>1 register dependent permutes As mentioned above, the P870 optimizes vrgather.vv and vcompress.vm for non-quadratic LMUL scaling, by only reading from source registers that actually need to be read from (including masking). This does however seem to introduce a large 8 cycle throughput penalty for all LMUL>1 register dependent permutes. It seems like all permutations that may read from one of two or more vector registers with runtime control, go through the same data-path. While the non-quadratic vrgather.vv implementation works and can improve performance the regular quadratic implementation pattern seems both easier to implement and gives more predictable performance. An efficient LMUL>1 vrgather.vv is a cool idea, but with such a high overhead it doesn't seem beneficial. The only real place where you'd need it because you can't simply unroll LMUL=1 vrgathers, is to implement something like expand, with viota+vrgather. But that should've been a seperate instruction, like vcompress, anyway. I suppose it may also accelerate bad code, but that's really not something we should be optimizing for. I'd recommend only implementing this scheme, if you can get close to or better than the quadratic implementation in the worst case. Having a slow LMUL=2 register dependent permutes is very unfortunate. Instruction timings The following are measured cycle averages when unrolling and looping over the given instruction, which should estimate the instruction throughput. The registers involved are randomized to usual values, that is floating point values are real numbers and not NaN/Inf, and instructions like vslide* stay within the vl range. Some valid vtype-instruction combinations are missing. Example measurement code for vadd.vx LMUL=1LMUL=4 bench_vaddvx_m1: m_nop li a0, WARMUP 1: vadd.vx v8,v16,t0 vadd.vx v9,v17,t1 vadd.vx v10,v18,t2 vadd.vx v11,v19,t3 vadd.vx v12,v20,t4 vadd.vx v13,v21,t5 vadd.vx v14,v22,t6 vadd.vx v15,v23,t7 addi a0, a0, -1 bnez a0, 1b li a0, LOOP rdcycle a1 1: .rept UNROLL vadd.vx v8,v16,t0 vadd.vx v9,v17,t1 vadd.vx v10,v18,t2 vadd.vx v11,v19,t3 vadd.vx v12,v20,t4 vadd.vx v13,v21,t5 vadd.vx v14,v22,t6 vadd.vx v15,v23,t7 .endr addi a0, a0, -1 bnez a0, 1b fence.i rdcycle a0 sub a0, a0, a1 ret bench_vaddvx_m4: m_nop li a0, WARMUP 1: vadd.vx v8,v16,t0 vadd.vx v12,v20,t4 vadd.vx v8,v16,t0 vadd.vx v12,v20,t4 vadd.vx v8,v16,t0 vadd.vx v12,v20,t4 vadd.vx v8,v16,t0 vadd.vx v12,v20,t4 addi a0, a0, -1 bnez a0, 1b li a0, LOOP rdcycle a1 1: .rept UNROLL vadd.vx v8,v16,t0 vadd.vx v12,v20,t4 vadd.vx v8,v16,t0 vadd.vx v12,v20,t4 vadd.vx v8,v16,t0 vadd.vx v12,v20,t4 vadd.vx v8,v16,t0 vadd.vx v12,v20,t4 .endr addi a0, a0, -1 bnez a0, 1b fence.i rdcycle a0 sub a0, a0, a1 ret Note: Some of the supported instructions weren't recorded. The benchmark also exposed a few bugs in rvv-bench that I didn't manage to fix in time, specifically the vrgather.vv throughput measurements do not actually use valid indices, instead look at vrgatherei16.vv where the indices are in bounds. Show fractional LMULs instructione8mf8e8mf4e8mf2e8m1e8m2e8m4e8m8e16mf8e16mf4e16mf2e16m1e16m2e16m4e16m8e32mf8e32mf4e32mf2e32m1e32m2e32m4e32m8e64mf8e64mf4e64mf2e64m1e64m2e64m4e64m8 vadd.vv v8,v16,v240.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vadd.vv v8,v16,v24,v0.t0.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vadd.vx v8,v16,t01.001.001.001.001.002.004.001.001.001.001.002.004.001.001.001.002.004.001.001.002.004.00 vadd.vx v8,v16,t0,v0.t1.001.001.001.001.002.004.001.001.001.001.002.004.001.001.001.002.004.001.001.002.004.00 vadd.vi v8,v16,130.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vadd.vi v8,v16,13,v0.t0.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vsub.vv v8,v16,v240.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vsub.vv v8,v16,v24,v0.t0.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vsub.vx v8,v16,t01.001.001.001.001.002.004.001.001.001.001.002.004.001.001.001.002.004.001.001.002.004.00 vsub.vx v8,v16,t0,v0.t1.001.001.001.001.002.004.001.001.001.001.002.004.001.001.001.002.004.001.001.002.004.00 vrsub.vx v8,v16,t01.001.001.001.001.002.004.001.001.001.001.002.004.001.001.001.002.004.001.001.002.004.00 vrsub.vx v8,v16,t0,v0.t1.001.001.001.001.002.004.001.001.001.001.002.004.001.001.001.002.004.001.001.002.004.00 vrsub.vi v8,v16,130.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vrsub.vi v8,v16,13,v0.t0.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vminu.vv v8,v16,v240.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vminu.vv v8,v16,v24,v0.t0.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vminu.vx v8,v16,t01.051.051.051.051.002.004.001.051.051.051.002.004.001.051.051.002.004.001.051.002.004.00 vminu.vx v8,v16,t0,v0.t1.051.051.051.051.002.004.001.051.051.051.002.004.001.051.051.002.004.001.051.002.004.00 vmin.vv v8,v16,v240.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vmin.vv v8,v16,v24,v0.t0.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vmin.vx v8,v16,t01.051.051.051.051.002.004.001.051.051.051.002.004.001.051.051.002.004.001.051.002.004.00 vmin.vx v8,v16,t0,v0.t1.051.051.051.051.002.004.001.051.051.051.002.004.001.051.051.002.004.001.051.002.004.00 vmaxu.vv v8,v16,v240.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vmaxu.vv v8,v16,v24,v0.t0.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vmaxu.vx v8,v16,t01.051.051.051.051.002.004.001.051.051.051.002.004.001.051.051.002.004.001.051.002.004.00 vmaxu.vx v8,v16,t0,v0.t1.051.051.051.051.002.004.001.051.051.051.002.004.001.051.051.002.004.001.051.002.004.00 vmax.vv v8,v16,v240.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vmax.vv v8,v16,v24,v0.t0.500.500.500.501.002.004.000.500.500.501.002.004.000.500.501.002.004.000.501.002.004.00 vmax.vx v8,v16,t01.051.051.051.051.002.004.001.051.051.051.002.004.001.051.051.002.004.001.051.002.004.00 vmax.vx v8,v16,t0,v0.t1.111.111.1 [truncated for AI cost control]