Hello Chenhui,
On 03.09.26 17:20, ChenhuiMo wrote: [...] > > I updated the benchmark so that the capped strategies also use memset() for > single-byte inputs. In that path, the memset() calls are split according to > the selected block size, with CHECK_FOR_INTERRUPTS() between chunks Thanks for the updated benchmark. For comparison, here are my numbers: # Debian 13.5 VM on an Apple M5 Max, 32MB of L2 cache / 24 MB of L3 # cache, 36 GB RAM, gcc 14.2.0 -O2 jan=# select * from repeat_bench(); source | repeats | output | master | doubling | nocap | cap512 | cap1k | cap2k | cap4k | cap8k | cap16k | cap32k | cap64k | cap128k | cap256k | cap512k | cap1m | cap2m | cap4m | cap8m | cap16m | fastest --------+-----------+----------+----------+----------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+--------------------------- 32 B | 2 | 64 B | 3 ns | 0.93x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | 0.82x | master 32 B | 4 | 128 B | 5 ns | 1.31x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | 1.13x | doubling 32 B | 6 | 192 B | 7 ns | 1.59x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | 1.42x | doubling 32 B | 7 | 224 B | 7 ns | 1.67x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | 1.50x | doubling 32 B | 8 | 256 B | 7 ns | 1.56x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | 1.65x | cap2k (median: nocap) 32 B | 9 | 288 B | 8 ns | 1.71x | 1.57x | 1.56x | 1.57x | 1.57x | 1.57x | 1.56x | 1.57x | 1.57x | 1.57x | 1.56x | 1.57x | 1.56x | 1.56x | 1.57x | 1.57x | 1.57x | 1.57x | doubling 32 B | 12 | 384 B | 9 ns | 1.27x | 1.52x | 1.44x | 1.55x | 1.40x | 1.56x | 1.55x | 1.56x | 1.55x | 1.46x | 1.40x | 1.46x | 1.43x | 1.55x | 1.40x | 1.56x | 1.55x | 1.56x | cap16k (median: cap1k) 32 B | 16 | 512 B | 12 ns | 1.43x | 1.24x | 1.24x | 1.24x | 1.25x | 1.24x | 1.25x | 1.25x | 1.25x | 1.24x | 1.25x | 1.25x | 1.24x | 1.25x | 1.25x | 1.25x | 1.24x | 1.25x | doubling (median: cap4m) 32 B | 64 | 2 KB | 49 ns | 2.29x | 2.25x | 2.24x | 2.27x | 2.26x | 2.27x | 2.26x | 2.27x | 2.26x | 2.26x | 2.26x | 2.27x | 2.27x | 2.26x | 2.26x | 2.27x | 2.26x | 2.27x | doubling (median: cap16m) 32 B | 4096 | 128 KB | 2.7 us | 2.55x | 2.84x | 2.35x | 2.44x | 2.44x | 2.47x | 2.58x | 2.50x | 2.51x | 2.51x | 2.54x | 2.54x | 2.51x | 2.53x | 2.51x | 2.52x | 2.50x | 2.50x | nocap (median: cap8k) 1 B | 16777216 | 16 MB | 14.8 ms | 119.90x | 120.23x | 71.77x | 73.30x | 74.02x | 67.44x | 39.32x | 61.44x | 71.05x | 93.60x | 105.89x | 112.77x | 116.40x | 118.54x | 119.58x | 120.19x | 120.19x | 120.35x | cap16m 10 B | 1677721 | 16 MB | 1.5 ms | 5.84x | 5.86x | 6.60x | 6.66x | 6.62x | 5.07x | 6.14x | 7.72x | 9.60x | 10.65x | 5.95x | 6.02x | 6.05x | 5.89x | 5.86x | 5.84x | 5.85x | 5.86x | cap64k 100 B | 167772 | 16 MB | 316.4 us | 1.27x | 1.27x | 1.42x | 1.47x | 1.37x | 1.11x | 1.47x | 1.90x | 2.26x | 2.38x | 1.30x | 1.30x | 1.31x | 1.28x | 1.27x | 1.27x | 1.27x | 1.27x | cap64k 1 KB | 16384 | 16 MB | 258.5 us | 1.04x | 1.04x | 1.01x | 1.01x | 1.20x | 1.09x | 0.95x | 1.18x | 1.45x | 1.80x | 1.68x | 1.06x | 1.07x | 1.06x | 1.04x | 1.04x | 1.03x | 1.04x | cap64k 64 KB | 256 | 16 MB | 204.0 us | 0.82x | 0.82x | 1.00x | 1.00x | 1.01x | 1.01x | 1.01x | 1.02x | 1.02x | 1.01x | 1.35x | 0.84x | 0.84x | 0.83x | 0.82x | 0.82x | 0.82x | 0.82x | cap128k 1 MB | 16 | 16 MB | 249.3 us | 0.99x | 0.99x | 1.00x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 0.99x | 0.99x | 0.99x | 0.99x | cap4k (median: cap256k) 1 B | 268435456 | 256 MB | 272.1 ms | 137.59x | 137.80x | 62.59x | 62.03x | 63.57x | 62.09x | 31.27x | 33.47x | 47.84x | 67.43x | 89.15x | 108.70x | 127.66x | 134.98x | 136.54x | 137.14x | 137.42x | 137.55x | nocap (median: cap16m) 10 B | 26843545 | 256 MB | 23.5 ms | 5.65x | 5.66x | 5.74x | 5.77x | 5.64x | 5.21x | 4.19x | 2.95x | 4.26x | 6.20x | 5.29x | 5.72x | 5.94x | 5.86x | 5.84x | 5.84x | 5.74x | 5.73x | cap64k 100 B | 2684354 | 256 MB | 5.2 ms | 1.25x | 1.25x | 1.26x | 1.28x | 1.24x | 0.74x | 0.97x | 0.74x | 1.08x | 1.57x | 1.29x | 1.31x | 1.32x | 1.29x | 1.29x | 1.29x | 1.27x | 1.26x | cap64k 1 KB | 262144 | 256 MB | 4.5 ms | 1.08x | 1.09x | 1.06x | 1.05x | 1.11x | 1.04x | 0.73x | 0.53x | 0.72x | 1.05x | 1.39x | 1.11x | 1.14x | 1.15x | 1.12x | 1.13x | 1.13x | 1.12x | cap128k 64 KB | 4096 | 256 MB | 4.5 ms | 1.08x | 1.09x | 1.02x | 1.00x | 1.00x | 1.01x | 1.01x | 0.99x | 1.00x | 0.99x | 1.34x | 1.08x | 1.14x | 1.14x | 1.12x | 1.13x | 1.12x | 1.12x | cap128k 1 MB | 256 | 256 MB | 4.1 ms | 0.97x | 0.98x | 1.02x | 1.04x | 1.04x | 1.04x | 1.04x | 1.04x | 1.04x | 1.04x | 1.06x | 1.04x | 1.04x | 1.04x | 1.02x | 1.02x | 1.02x | 1.01x | cap128k (median: cap64k) 1 B | 1000 | 1000 B | 1.3 us | 166.38x | 157.59x | 153.17x | 157.57x | 156.83x | 156.83x | 157.57x | 157.57x | 157.57x | 157.59x | 157.57x | 157.57x | 156.83x | 157.57x | 157.59x | 156.83x | 156.83x | 156.83x | doubling 1 B | 1000000 | 976.6 KB | 870.5 us | 112.32x | 112.94x | 72.54x | 75.42x | 76.25x | 75.42x | 49.63x | 70.82x | 87.05x | 99.48x | 107.70x | 113.55x | 112.32x | 112.94x | 112.92x | 112.94x | 112.94x | 113.54x | cap256k (median: nocap) (24 rows) # Debian 13.5 on an Intel Pentium Silver J5005, 4 MB L2 cache, no L3 # cache, 16 GB RAM, gcc 14.2.0 -O2 jan=# select * from repeat_bench(); source | repeats | output | master | doubling | nocap | cap512 | cap1k | cap2k | cap4k | cap8k | cap16k | cap32k | cap64k | cap128k | cap256k | cap512k | cap1m | cap2m | cap4m | cap8m | cap16m | fastest --------+-----------+----------+-----------+----------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+------------------------- 32 B | 2 | 64 B | 13 ns | 1.06x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | 0.81x | doubling 32 B | 4 | 128 B | 22 ns | 1.15x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | 1.02x | doubling 32 B | 6 | 192 B | 31 ns | 1.19x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | 1.09x | doubling 32 B | 7 | 224 B | 36 ns | 1.39x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | 1.27x | doubling 32 B | 8 | 256 B | 42 ns | 1.57x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | 1.43x | doubling 32 B | 9 | 288 B | 47 ns | 1.46x | 1.36x | 1.35x | 1.35x | 1.35x | 1.35x | 1.35x | 1.36x | 1.36x | 1.35x | 1.35x | 1.35x | 1.36x | 1.36x | 1.36x | 1.36x | 1.36x | 1.36x | doubling 32 B | 12 | 384 B | 61 ns | 1.81x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | 1.68x | doubling 32 B | 16 | 512 B | 84 ns | 2.26x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | 2.06x | doubling 32 B | 64 | 2 KB | 316 ns | 3.77x | 3.56x | 3.29x | 3.57x | 3.56x | 3.55x | 3.55x | 3.56x | 3.55x | 3.56x | 3.55x | 3.55x | 3.55x | 3.55x | 3.55x | 3.55x | 3.55x | 3.55x | doubling 32 B | 4096 | 128 KB | 19.1 us | 2.06x | 2.06x | 2.65x | 2.83x | 2.97x | 2.80x | 2.86x | 2.57x | 2.10x | 2.06x | 2.07x | 2.07x | 2.07x | 2.06x | 2.06x | 2.07x | 2.06x | 2.07x | cap2k 1 B | 16777216 | 16 MB | 115.4 ms | 62.63x | 62.87x | 48.66x | 48.17x | 46.47x | 46.12x | 46.15x | 45.99x | 46.26x | 46.42x | 45.77x | 46.02x | 45.57x | 53.15x | 62.82x | 62.85x | 64.64x | 63.37x | cap8m (median: cap16m) 10 B | 1677721 | 16 MB | 12.8 ms | 4.69x | 4.84x | 5.81x | 5.43x | 5.56x | 5.51x | 5.50x | 5.44x | 5.29x | 5.29x | 5.28x | 5.29x | 5.31x | 6.08x | 6.31x | 4.92x | 4.92x | 4.87x | cap2m 100 B | 167772 | 16 MB | 2.2 ms | 0.82x | 0.85x | 1.01x | 0.98x | 0.91x | 0.93x | 0.93x | 0.90x | 0.90x | 0.89x | 0.89x | 0.90x | 0.90x | 1.01x | 1.03x | 0.86x | 0.85x | 0.85x | cap2m 1 KB | 16384 | 16 MB | 2.2 ms | 0.83x | 0.85x | 1.00x | 0.98x | 0.76x | 0.96x | 0.96x | 0.95x | 0.92x | 0.91x | 0.92x | 0.92x | 0.91x | 1.01x | 1.10x | 0.92x | 0.87x | 0.85x | cap2m 64 KB | 256 | 16 MB | 2.4 ms | 0.90x | 0.91x | 1.00x | 1.00x | 0.97x | 0.97x | 0.97x | 0.97x | 0.97x | 0.97x | 0.98x | 0.98x | 0.98x | 1.07x | 1.18x | 0.98x | 0.92x | 0.91x | cap2m 1 MB | 16 | 16 MB | 2.3 ms | 0.87x | 0.85x | 0.98x | 1.06x | 1.06x | 1.06x | 1.06x | 1.06x | 1.06x | 1.06x | 1.06x | 1.06x | 1.06x | 1.06x | 1.05x | 0.91x | 0.83x | 0.85x | cap1k (median: cap16k) 1 B | 268435456 | 256 MB | 1863.4 ms | 63.24x | 64.65x | 49.51x | 49.87x | 49.36x | 49.32x | 48.93x | 49.09x | 48.95x | 49.08x | 49.03x | 48.93x | 49.00x | 61.96x | 63.08x | 62.97x | 63.22x | 63.51x | nocap 10 B | 26843545 | 256 MB | 207.6 ms | 4.45x | 4.47x | 5.43x | 5.22x | 5.49x | 5.50x | 5.44x | 5.38x | 5.25x | 5.20x | 5.24x | 5.22x | 5.21x | 6.37x | 6.38x | 4.60x | 4.53x | 4.47x | cap2m (median: cap1m) 100 B | 2684354 | 256 MB | 38.7 ms | 0.82x | 0.83x | 1.01x | 0.97x | 1.02x | 1.02x | 1.01x | 0.98x | 0.98x | 0.97x | 0.98x | 0.98x | 0.97x | 1.18x | 1.16x | 0.85x | 0.84x | 0.83x | cap1m 1 KB | 262144 | 256 MB | 37.8 ms | 0.81x | 0.81x | 1.00x | 1.00x | 0.70x | 1.00x | 0.99x | 0.99x | 0.95x | 0.95x | 0.95x | 0.95x | 0.95x | 1.16x | 1.16x | 1.03x | 0.82x | 0.82x | cap2m (median: cap1m) 64 KB | 4096 | 256 MB | 39.1 ms | 0.83x | 0.84x | 1.00x | 0.99x | 0.99x | 0.99x | 0.99x | 0.99x | 0.99x | 0.99x | 0.99x | 0.98x | 0.98x | 1.20x | 1.20x | 1.00x | 0.84x | 0.85x | cap2m (median: cap1m) 1 MB | 256 | 256 MB | 32.6 ms | 0.70x | 0.69x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.01x | 1.00x | 0.82x | 0.71x | 0.71x | cap128k 1 B | 1000 | 1000 B | 6.8 us | 240.97x | 225.32x | 201.53x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | 225.38x | doubling 1 B | 1000000 | 976.6 KB | 6.8 ms | 134.42x | 134.55x | 124.58x | 126.56x | 132.95x | 128.51x | 131.56x | 133.10x | 133.85x | 134.27x | 134.37x | 134.51x | 134.58x | 134.53x | 134.55x | 134.57x | 134.52x | 134.54x | cap512k (median: nocap) (24 rows) To understand the different cap behavior on our systems better, could you share the output of the following command from your systems? ld.so --list-tunables | grep non_temporal On my J5005, it looks as follows: $ ld.so --list-tunables | grep non_temporal glibc.cpu.x86_memset_non_temporal_threshold: 0x100000 (min: 0x4040, max: 0xffffffffffffffff) glibc.cpu.x86_non_temporal_threshold: 0x100000 (min: 0x4040, max: 0xfffffffffffffff) The preferred cap on the J5005 is the first one at or above this threshold (0x100000 = 1 MB). Does this match the behavior on your systems as well? On ARM, this tunable isn't present. > The corrected single-byte results do not change the overall conclusion. For > multi-byte inputs, the preferred cap on this machine still varies noticeably > with the total output size. In particular, the 16 MB cases tend to prefer > relatively small caps, while the 256 MB cases consistently prefer cap16m. > > For the single-byte cases, splitting the memset() into capped chunks also does > not seem to introduce a significant performance penalty. In some cases, the > larger capped memset variants are even slightly faster than a single large > memset(). Unfortunately, on the M5 Max it does. Comparing the worst chunked variant (cap8k in all three cases) against nocap, which does the same work in a single memset(): output cap8k nocap ratio 16 MB 39.32x 120.23x 3.1x 256 MB 31.27x 137.80x 4.4x 976.6 KB 49.63x 112.94x 2.3x To move this patch forward, would you consider proposing the single-byte memset() as a separate, smaller patch first? It is a large win with no regression in the benchmarks we have executed. It is also independent of the block size question. So, it could go in while the doubling strategy needs more investigation. Also, the early return for count == 0 || slen == 0 is a second change that could be committed independently of the cap discussion. Best regards Jan -- Jan Nidzwetzki PlanetScale Postgres Core Team
