Re: Re:[PATCH] Speed up repeat() for larger counts

From: Jan Nidzwetzki <jan(at)planetscale(dot)com>
To: ChenhuiMo <chenhuimo(dot)mch(at)qq(dot)com>, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>
Cc: "jeevan(dot)chalke" <jeevan(dot)chalke(at)enterprisedb(dot)com>, Heikki Linnakangas <hlinnaka(at)iki(dot)fi>
Subject: Re: Re:[PATCH] Speed up repeat() for larger counts
Date: 2026-09-08 10:29:26
Message-ID: 1fe7eff1-b402-48b0-a1d1-48e5e79f8df7@planetscale.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hello Chenhui,

I tried to find the reason why the preferred cap in the benchmark
differs so much between our machines, and I came up with a theory
that explains the behavior.

On 04.09.26 15:51, Jan Nidzwetzki wrote:
> 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?

glibc tunable
=============

On x86, glibc switches memcpy() to non-temporal stores above
x86_non_temporal_threshold (bypassing the CPU caches and writing
data directly to the system memory).

This value is dynamically calculated based on the characteristics
of the machine. However, it can be adjusted manually, like:

GLIBC_TUNABLES="glibc.cpu.x86_non_temporal_threshold=$value\
:glibc.cpu.x86_memset_non_temporal_threshold=$value"

I did an experiment (outside of PostgreSQL) where I forced
certain thresholds and repeated the benchmark. Changing that
value moves the preferred cap with it. On the J5005, with a
256 MB output:

set threshold 1 KB source 100 B source
64 KB cap64k cap64k
256 KB cap256k cap256k
1 MB cap1m cap1m
4 MB none none
disabled none none

The speedup always starts at the first cap at or above the threshold,
when non-temporal stores are used and the cache is bypassed.

At a 4 MB threshold, the effect disappears entirely. A 4 MB block
is the whole L2 on this machine, so no size is both large enough
for non-temporal stores and small enough to stay cached.

With the non-temporal path disabled, the row is flat as well, and
uncapped doubling falls below the current implementation.

Since this is adjustable through GLIBC_TUNABLES, the best cap is not
only machine-dependent; it is settable from the environment.

We could potentially use something like __builtin_nontemporal_store()
to avoid the glibc threshold and force non-temporal stores. However,
this is compiler- and architecture-specific, and I don't think it is
worth optimizing this particular code path that way.

What do you think about the theory?

Personally, I would go with the early return and memset() optimization
as outlined in my last mail, since they show clear improvements in all
tested environments. The doubling / capping approach shows good
results in many situations, but is harder to implement, since picking
a wrong cap leads to regressions.

However, I am still curious what other people will see when they
repeat the benchmarks.

Best regards
Jan

--
Jan Nidzwetzki
PlanetScale Postgres Core Team

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Hayato Kuroda (Fujitsu) 2026-09-08 10:52:35 RE: pg_createsubscriber does not check output_plugin_libraries
Previous Message Alvaro Herrera 2026-09-08 10:27:19 Re: REPACK (ANALYZE) within transaction block segfaults