[RVV] add rvv f32 kernels for CHW ibilinear#10771
Draft
leduclean wants to merge 2 commits into
Draft
Conversation
There was no benchmark for this microkernel family, so changes to the CHW
bilinear kernels could not be measured.
Build the indirection buffer and the interpolation coefficients with
xnn_indirection_init_resize_bilinear2d_chw_f32 -- the operator's own code, as
bench/f32-dwconv.cc already does for dwconv -- so the benchmark cannot drift
from the layout the operator actually produces. Two properties of that layout
dominate the memory behaviour and are easy to get wrong by hand: the two
indirection pointers of a pixel are one input row apart, and channel planes
are disjoint, with input_increment == input_height * input_width *
sizeof(float).
Parameterize by {InW, InH, OutW, OutH, C} and reuse the decoder shapes of
bench/operators/resize-bilinear-nhwc.cc, so the microkernel numbers stay
comparable with the operator benchmark, plus a few working sets larger than
cache.
Rotate buffers across iterations following the f32-dwconv idiom. The input
rotates via input_offset, which is the operator's own rebasing mechanism.
Register every f32-ibilinear-chw ukernel: scalar p1/p2/p4, neon and neonfma
p4/p8/p16, sse p4/p8, and wasmsimd p4/p8.
CHW counterpart of the f32-ibilinear RVV kernels added in google#10682. Up to 1.24x over the scalar_p4 this replaces, 1.1x geomean on the decoder shapes that motivated the kernel. Measured on K230 (C908, VLEN=128, in-order, single core, ~1.6 GHz) with bench/f32-ibilinear-chw: decoder shapes (6): geomean 1.1x (0.98x .. 1.24x) large shapes (5): geomean 0.96x (0.95x .. 0.98x) all shapes (11): geomean 1.03x The four taps are gathered with vluxseg2ei64, indexed off the indirection pointers. The kernels are generated from rvv.c.in over two parameters: CHANNEL_TILE, which amortizes the indirection and weight re-reads across a block and LMUL. Select rvv_2x2v when xnn_arch_riscv_vector is present, and keep scalar_p4 otherwise. rvv_2x2v dominates the other three variants on all 11 shapes, which is why it is the one selected; the others are kept as the family that makes that choice reproducible on other cores, as with neon p4/p8/p16. The gain is bounded by the gather, not the ALU: rvv_2x2v retires 6.3x fewer instructions than scalar_p4, but each costs 6.7x more cycles (CPI ~9 against ~1.4), because vluxseg2ei64 serializes its element accesses on this core. A core with a better indexed-segment-load path should see more of the 6.3x. Signed-off-by: Le Duc Léandre <lleduc@kalrayinc.com>
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CHW counterpart of the
f32-ibilinearRVV kernels added in #10682.Tested on K230 (C908, VLEN=128, in-order, single core, ~1.6 GHz), median of 3 repetitions, CV below 0.7% on every row.
Scalarisscalar_p4, which is whatibilinear-chw-config.cselects on RISC-V today and what this PR replaces.Geomean 1.071x on the six decoder shapes, 0.952x on the five large ones, 1.015x overall. The decoder shapes are the ones the CHW path actually serves (sparse inference, segmentation decoders); the large shapes are deliberate stress cases and are reported as regressions rather than omitted.
Why the gain is modest, unlike #10682's 4-6x. NHWC has a contiguous dimension:
f32-ibilinear-rvv-u2vvectorizes along channels with plain unit-stridevle32, no gather at all. CHW is transposed the four taps of a pixel sit at arbitrary addresses so the kernel must gather.On this core
vluxseg2ei64serializes its element accesses:rvv_2x2vretires 6.3x fewer instructions thanscalar_p4, but each costs 6.7x more cycles (CPI ~9 against ~1.4). The kernel is bound by the gather, not the ALU.Two alternatives were measured and dropped: staging the taps through scalar loads NEON-style (0.75-0.82x RVV has no lane-insert from memory, so the stack round trip costs more than the gather saves), and vectorizing across channels with strided loads (0.05x on any power-of-two spatial size, from L1 set aliasing when
input_incrementis a multiple of the way size).All numbers are from one core. They should not be read as a statement about RVV in general a core with a parallel gather could reorder the whole table.