On Tue, 30 Jun 2026 17:09:26 GMT, Andrew Haley <[email protected]> wrote:
>> The current organization is intentional. I considered carrying the >> lane-position weights across loop iterations, similar to the x86_64 >> implementation, as well as the previous AArch64 implementation. >> >> The previous AArch64 implementation worked well on some cores by creating >> multiple smaller multiplication chains to help hide multiply latency for >> long arrays. In my measurements on recent Arm Neoverse cores (for example, >> Neoverse V2 and N2), reducing each 16-element block to a scalar block hash >> and then performing the scalar Horner update exposed more independent work >> to the scheduler and produced better performance than carrying vector >> accumulators across loop iterations. >> >> The horizontal reduction is relatively inexpensive on these cores and can >> overlap with the vector multiply/add work, making the per-block reduction a >> good tradeoff. > > But what's the point of exposing more independent work to the scheduler, when > that work doesn't do anything useful? > > Do you have the version without the reduction each time so we can compare it > on other implementations? Yes. I implemented that version for comparison. I have two standalone AArch64/NEON P16 implementations: reduce_loop: performs the horizontal reduction inside every 16-byte iteration. reduce_end: keeps 16 logical accumulators (four NEON vectors) across the loop and performs a single reduction after processing all blocks (AArch64 current stub is a smarter(less vector registers) implementation of this algorithm too). For this experiment, both implementations assume: input length is a multiple of 16, no tail handling, seed = 0. On my test system, both implementations produce the same result as the scalar reference for the tested lengths under those assumptions. At 64 KB, I measured: reduce_loop: 0.0976 ns/byte reduce_end : 0.1316 ns/byte reduce_loop was consistently faster across all tested lengths from 16 bytes up to 64 KB. I've also included a small benchmark driver and build/run instructions so others can reproduce the comparison: [hash.zip](https://github.com/user-attachments/files/29598153/hash.zip) g++ -O3 -march=armv8-a -o hash_bench \ driver.cpp \ hash_jdk_neon_reduce_loop.S \ hash_jdk_neon_reduce_end.S ./hash_bench This is detailed output: === Correctness display only; mismatch is OK for this experiment === len= 16 scalar=0xCAD5BD38 loop=0xCAD5BD38 end=0xCAD5BD38 len= 32 scalar=0x20787A70 loop=0x20787A70 end=0x20787A70 len= 64 scalar=0xB196B3E0 loop=0xB196B3E0 end=0xB196B3E0 len= 128 scalar=0x5E42E8C0 loop=0x5E42E8C0 end=0x5E42E8C0 len= 256 scalar=0xF24DEF80 loop=0xF24DEF80 end=0xF24DEF80 len= 512 scalar=0x07ABDF00 loop=0x07ABDF00 end=0x07ABDF00 len= 1024 scalar=0x9B97BE00 loop=0x9B97BE00 end=0x9B97BE00 len= 4096 scalar=0x945EF800 loop=0x945EF800 end=0x945EF800 len=16384 scalar=0xB17BE000 loop=0xB17BE000 end=0xB17BE000 len=65536 scalar=0xC5EF8000 loop=0xC5EF8000 end=0xC5EF8000 #=== Performance === Implementation len iters ns/call ns/byte MB/s -------------------------------------------------------------------------------- scalar 16 50000 7.03 0.4397 2274.46 reduce_loop 16 50000 2.44 0.1526 6553.29 reduce_end 16 50000 3.05 0.1908 5240.16 scalar 32 50000 16.99 0.5309 1883.59 reduce_loop 32 50000 3.97 0.1241 8060.90 reduce_end 32 50000 4.28 0.1337 7478.56 scalar 64 50000 43.93 0.6865 1456.76 reduce_loop 64 50000 6.48 0.1012 9881.76 reduce_end 64 50000 7.12 0.1112 8993.92 scalar 128 50000 104.72 0.8181 1222.35 reduce_loop 128 50000 12.74 0.0996 10044.02 reduce_end 128 50000 13.54 0.1058 9451.67 scalar 256 50000 223.33 0.8724 1146.27 reduce_loop 256 50000 25.44 0.0994 10064.75 reduce_end 256 50000 29.77 0.1163 8598.75 scalar 512 50000 460.43 0.8993 1112.02 reduce_loop 512 50000 50.87 0.0993 10065.77 reduce_end 512 50000 64.19 0.1254 7976.02 scalar 1024 20000 934.85 0.9129 1095.36 reduce_loop 1024 20000 101.74 0.0994 10064.82 reduce_end 1024 20000 133.00 0.1299 7699.32 scalar 4096 20000 3782.10 0.9234 1083.00 reduce_loop 4096 20000 408.89 0.0998 10017.40 reduce_end 4096 20000 544.38 0.1329 7524.16 scalar 16384 5000 15164.99 0.9256 1080.38 reduce_loop 16384 5000 1629.30 0.0994 10055.84 reduce_end 16384 5000 2187.44 0.1335 7490.04 scalar 65536 5000 60574.87 0.9243 1081.90 reduce_loop 65536 5000 6500.62 0.0992 10081.49 reduce_end 65536 5000 8786.19 0.1341 7458.98 === Summary at 64KB === Metric scalar loop end -------------------------------------------------------- ns/byte 0.9247 0.0992 0.1340 MB/s 1081.4 10079.7 7463.5 speedup vs scalar 1.0 9.3 6.9 speedup vs loop - 1.00 0.74 ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/31674#discussion_r3513070118
