[PATCH] rtld: cache cpuid results on the stack for intel
Florian Weimer
fweimer@redhat.com
Thu Apr 16 10:03:37 GMT 2026
* Fabian Rast:
> On Mon Feb 2, 2026 at 10:26 AM CET, Florian Weimer wrote:
>> * Fabian Rast:
>>
>>> Does anyone have experience with slow cpuid that could explain whats going =
>>> on?
>>
>> In the slow case, are you running on bare metal, or is it virtualized?
>
> This is on bare metal. An Intel(R) Xeon(R) Gold 6430.
>
> My supervisor has access to more hardware - here are the results for
> different processors:
>
> model name : Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz
> ./selfexec_patch 1000 ran 1.03 ± 0.01 times faster than ./selfexec_master 1000
>
> model name : Intel(R) Core(TM) i9-7900X CPU @ 3.30GHz
> ./selfexec_patch 1000 ran 1.03 ± 0.02 times faster than ./selfexec_master 1000
>
> model name : Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
> ./selfexec_patch 1000 ran 1.02 ± 0.01 times faster than ./selfexec_master 1000
>
> Note that these binaries are statically linked, which amplifies the
> effect (less loader overhead...). For comparison, here is the Intel(R)
> Xeon(R) Gold 6430 again:
> ./selfexec_patch 1000 ran 1.23 ± 0.04 times faster than ./selfexec_master 1000
>
> It looks like there is something special going on with the 6430, or I
> am missing something... It would be great if someone that has access to
> a xeon gold 6430 could reproduce this.
I've been struggling with a simple reproducer for this. I can reproduce
the performance improvement from your caching patch even with dynamic
linking (although it less pronounced). However, I have failed to build
something standalone using __cpuid_count calls that shows the same
performance behavior.
In perf, I can see that a significant part of the execution time in your
reproducer is attributed to CPUID, and >20% of all cycles (userspace and
kernel) end up in the CPUID/machine detection code in ld.so. On its
own, this could be a sampling artifact because CPUID is a serializing
instruction. However, your caching change shows that there are
performance improvements to be had. And applying Amdahl's law in
reverse suggests that these CPUID instructions must take significant
time for real.
My best guess at this point is that the CPUID instructions negatively
interact with the loops and conditional branching in the ld.so code.
I want to understand this better. The same CPUID instructions execute
much faster in other contexts even on the Xeon 6430 CPU, so maybe we can
replicate these other conditions in ld.so and eliminate even more
overhead.
Ideas very welcome.
>>> The man page for getauxval defines several tags regarding the same
>>> information that is queried in dl_init_cacheinfo, but the loader does
>>> not try to read them and my kernel does not set them. If almost every
>>> program wants this information, why is getting it through the aux
>>> vector not the preferred approach?
>>
>> The tags are not used on x86-64, see “LD_SHOW_AUXV=1 /bin/true”. The
>> values the kernel computes would likely be different.
>
> Good to know. Maybe it is worth mentioning that the values can also differ
> between processes when they are determined through cpuid during startup.
> For example, my "AMD Ryzen AI 9 365" has a hybrid architecture:
> The P-cores report 16MiB shared cache, but the E-cores only 8MiB.
> So I suppose if a process that started up on an E-core gets scheduled
> to a P-core with more shared cache later, the values will be incorrect anyways.
Would you pst ld.so --list-diagnostics output for the CPU? I assumed
that the leaves we actually query in ld.so would not behave like that.
Do AMD CPUs even have P-cores and E-cores?
Thanks,
Florian
More information about the Libc-alpha
mailing list