On Thu, 30 Jul 2026 02:02:54 GMT, Eric Fang <[email protected]> wrote:

> VectorMask.lastTrue() returns the index of the last (highest-numbered) lane 
> that is set in the mask, or -1 if no lane is set.
> 
> Currently on AArch64 with SVE enabled, this API is implemented on a predicate 
> register via the sequence `rev + brkb + cntp + movw + subw`. There is no 
> native SVE predicate instruction for "last active lane index". This 
> implementation doesn't perform very well because it heavily relies on the M 
> pipeline, and the dependency chain between instructions is too long.
> 
> There is a SVE `lastb` instruction [1] that can extract the last active 
> element from a vector register, and we can use this instruction to implement 
> this API. As follow:
> 
> // vtmp = 0, 1, 2, ...
> sve_index(vtmp, size, 0, 1);
> // dst = last true or the highest-numbered element if src is all false
> sve_lastb(dst, size, src, vtmp);
> // Zero flag = 1 iff no active lane
> sve_ptest(ptrue, src);
> // active: keep; else -1
> csinvw(dst, dst, zr, Assembler::NE);
> 
> This implementation is more evenly distributed across the different pipelines 
> and exhibits better parallelism. Thus has better performance.
> 
> On a Nvidia Grace (Neoverse-V2) machine with 128-bit SVE2:
> 
> Benchmark             inputs  Unit    Before          Error   After           
> Error   Uplift
> testLastTrueByte      1       ops/ms  1047092.6       33773.0 1132866.9       
> 29053.6 1.08
> testLastTrueByte      2       ops/ms  1032945.0       18573.7 1152164.6       
> 21749.0 1.12
> testLastTrueByte      3       ops/ms  1032476.9       25744.7 1136212.3       
> 33285.8 1.10
> testLastTrueDouble    1       ops/ms  987207.0        6460.4  1126175.6       
> 5973.0  1.14
> testLastTrueDouble    2       ops/ms  987724.9        5854.1  1126033.5       
> 3558.7  1.14
> testLastTrueDouble    3       ops/ms  985976.2        3525.5  1128522.9       
> 2284.2  1.14
> testLastTrueFloat     1       ops/ms  978225.9        12308.3 1129334.8       
> 7655.7  1.15
> testLastTrueFloat     2       ops/ms  980388.5        15776.9 1119524.3       
> 25849.4 1.14
> testLastTrueFloat     3       ops/ms  988192.5        3414.6  1129177.4       
> 7393.8  1.14
> testLastTrueInt               1       ops/ms  979679.8        9878.7  
> 1125093.3       7802.7  1.15
> testLastTrueInt               2       ops/ms  987964.7        3320.0  
> 1116457.0       12490.9 1.13
> testLastTrueInt               3       ops/ms  982718.4        6455.4  
> 1131456.3       8357.6  1.15
> testLastTrueLong      1       ops/ms  1009693.7       3160.9  1113229.0       
> 18412.3 1.10
> testLastTrueLong      2       ops/ms  1003385.8       6314.5  1126162.2       
> 6872.6  1.12
> testLastTrueLong      3       ops/ms  998882.0        10596.3 1126631.1       
> 2575.0  1.13
> testLastTrueShort     1       ops/ms  981657.4        16531.1 1110119.0       
> 22422.8 1.13
> testLastTrueShort     2       ops/ms  989732.1        5418.9  1120666.7       
> 14837.0 1.13
> testLastTrueShort     3       ops/ms  993479.7        20890.1 1113419.3       
> 9783.8  1.12
> 
> 
> On an AWS Graviton3 (Neoverse-V1) machine with 256-bit SVE1:
> 
> Benchmark           bits inputs Unit  Before          Error   After...

Hi @theRealAph any comment on this PR, it's a pure AArch64 backend optimization.

-------------

PR Comment: https://git.openjdk.org/jdk/pull/32094#issuecomment-5263127787

Reply via email to