4 ms·
There are two dedicated CPU feature flags to indicate that REP STOS/MOV are fast and usable as short instruction sequence for memset/memcpy. Having to hand-roll
by the8472 3y ago
There are two dedicated CPU feature flags to indicate that REP STOS/MOV are fast and usable as short instruction sequence for memset/memcpy.
Having to hand-roll optimized routines for each new CPU generation has been an ongoing pain for decades.
And yet here we are again. Shouldn't this be part of some timing testsuite of CPU vendors by now?
- giancarlostoro 3y agoSo correct me if I am wrong but does this mean you need to compile two executables for a specific compile time build? Or is it just you need to compile it from specific hardware? Wondering what the fix would be, some sort of runtime check?
- immibis 3y agoglibc has the ability to dynamically link a different version of a function based on the CPU.
- dralley 3y agoGlibc supports runtime selection of different optimized paths, yes. There was a recent discussion about a security vulnerability in that feature (discussion https://news.ycombinator.com/item?id=37756357 https://news.ycombinator.com/item?id=37756357), but in essence this is exactly the kind of thing it's useful for.
- fweimer 3y agoThe exact nature of the fix is unclear at present. During dynamic linking, glibc picks a memcpy implementation which seems most appropriate for the current machine. We have about 13 different implementations just for x86-64. We could add another one for current(ish) AMD CPUs, select a different existing implementation for them, or change the default for a configurable cutover point in a parameterized implementation.
- saagarjha 3y agoThis code is in the kernel, so dynamic linking and glibc is not really relevant.
- JonChesterfield 3y agoIt's the same design space. If glibc has ended up with 13 versions for x64, the kernel probably has a similar number. It's an argument by analogy for how much of an annoyance this is.
- saagarjha 3y agoNo, because the kernel has particular code size and ISA restrictions.
- fweimer 3y agoAdmittedly I had missed that. The kernel has self-patching mechanisms for doing effectively the same thing (although I don't know if it has ever been applied to memcpy before).
- ww520 3y agoSince the CPU instructions are the same, instruction patching at startup or install time can be used. Just patch in the correct instructions for the respective hardware.
- saagarjha 3y agoThis is generally a bad idea because it requires code modification, which has security implications. Most implementations will bring in multiple implementations and select the right one at startup (amortizing the indirect call into something like the GOT which already exists).
- JonChesterfield 3y agoThere are security hazards around writable + executable code. They don't apply to patching before execution (e.g. the install step) since nothing needs to be executed at that point. I don't think the security concerns apply during load time either - what does it matter if the text section is edited before it gets marked read-only&executable? It just means you're running a slightly different program, exactly as if it was edited during install. In the memcpy case, where the library call is probably in a dynamically linked library anyway, it's particularly trivial to bind to one of N implementations of memcpy at load time. That only patches code if library calls are usually implemented that way. Patching .text does tend to mess up using the same shared pages across multiple executables though which is a shame, and somewhat argues for install time specialisation.
- saagarjha 3y agoOn certain platforms, it would break code signatures if they are tied to the pages the code is on.
- andrekandre 3y ago> On certain platforms, it would break code signatures macos?
- the8472 3y agoThe sibling comments mention the hardware specific dynamic linking in glibc that's used for function calls. But if your compiler inlines memcpy (usually for short, fixed-sized copies) into the binary then yes you'll have to compile it for a specific CPU to get optimal performance. But that's true for all target-dependent optimizations. More broadly compatible routines will still work on newer CPUs, they just won yield the best performance. It still would be nice if such central routines could just be compiled to the REP-prefixed instructions and would deliver (near-)optimal performance so we could stop worrying about that particular part.
- saagarjha 3y agoThey are, glibc already has an ERMS code path for memcpy.
- the8472 3y agoDid you miss the part about inlined calls? I was saying that if it weren't for these issues we could have them turn into a single instruction without worrying about perf.
- gpderetta 3y agoI theory the compiler could always statically inline a memcpy to a (possibly nop-padded) rep mov. Then the dynamic linker could dynamically patch the instructions to a call to an out of line function if rep mov is known not to be optimal to the actual CPU the code is running. The reverse (patching a call to memmove with a rep mov) is also possible.
- saagarjha 3y agoGenerally the compiler will not inline a memcpy if it doesn't know the size it is dealing with.
- dzaima 3y agoSome quick searching gives that FSRM is used for at least 128 bytes or so (ERMS for ≥~2048 bytes for reference); in base x86 (i.e. SSE2) that's 8 loads & 8 stores, ~62 bytes of code. At that point calling into a library function isn't too unreasonable (at the very least it could utilize AVX and cut that in half to 4 loads+4 stores, though at the cost of function call overhead & some (likely correctly-predicted) branches).
- mike_hock 3y agoYou'd think the CPU vendor knows their CPU best. If there's a faster "software" implementation, why doesn't REP MOVS at least do the same thing in microcode?
- gpderetta 3y agoI'm completely making stuff up here, but I wonder if this is the effect of some last minute (or even post-release, via ucode update) bug fix, where page aligned fast rep movs had issues or were subject to some attack and got disabled.