8 ms·
The surprising subtleties of zeroing a register (2012)
- jsnell 5y ago(2012) The most active previous discussion: https://news.ycombinator.com/item?id=19262249 https://news.ycombinator.com/item?id=19262249
- dang 5y agoThanks! Expanded list: The Surprising Subtleties of Zeroing a Register (2013) - https://news.ycombinator.com/item?id=19262249 https://news.ycombinator.com/item?id=19262249 - Feb 2019 (22 comments) The Surprising Subtleties of Zeroing a Register (2012) - https://news.ycombinator.com/item?id=11057679 https://news.ycombinator.com/item?id=11057679 - Feb 2016 (3 comments) The Surprising Subtleties of Zeroing a Register - https://news.ycombinator.com/item?id=6312266 https://news.ycombinator.com/item?id=6312266 - Sept 2013 (1 comment)
- billfruit 5y agoWhat ahout FP registers? Is there special instructions to Zero them?
- woodruffw 5y agoFor x87, you can use FLDZ to load `+0.0` onto the register stack. My guess is that neither Intel nor AMD has tried particularly hard to optimize that, given x87's legacy status and weird register semantics (see: them being a pseudo-stack). For SIMD-based FP, you can use VZEROUPPER and VZEROALL if you want to clear all {X,Y,Z}MM state. For a single register, I believe PXOR is still the common idiom, mirroring `XOR EAX, EAX`.
- titzer 5y ago> For x87 Should read: for x87, unless you are doing this for fun, shoot yourself in the face and instead use SSE because no one in their right mind should be writing x87 code in 2021! Please help this architectural misfeature die, which should have happened decades ago.
- woodruffw 5y ago> Should read: for x87, unless you are doing this for fun, shoot yourself in the face and instead use SSE because no one in their right mind should be writing x87 code in 2021! My information is pretty old, but IIRC x87 still has specialized instructions for sin/cos/tan that are sometimes more performant than their equivalent implementations in SSE. x87 instructions are also very small, so trig-heavy workloads where I$ is a measurable performance component might unfortunately still be a good fit for x87.
- adrian_b 5y agoOn recent Intel/AMD CPUs the x87 transcedental functions are usually much slower than their equivalents using SSE/AVX instructions. For future CPUs, it is expected that the performance gap will increase. The x87 trigonometric functions require typically between 100 and 200 clock cycles. During that time a recent CPU can execute 200 to 500 instructions, enough to compute many values of a trigonometric function (using a polynomial approximation). When SIMD instructions can be used, several tens of values of a function could be computed during a single x87 instruction.
- lifthrasiir 5y agox87 trig instructions are pretty inaccurate even in the supported range mainly because of faulty range reduction [1] and can't be vectorized at all. If you have trig-heavy workloads nowadays you would want SIMD libm, not x87. [1] http://notabs.org/fpuaccuracy/ http://notabs.org/fpuaccuracy/
- cogman10 5y agoWhy can't the trig instructions be vectorized? I've never quiet understood why SSE/AVX didn't add trig functions to finally kill off any argument for using x87.
- adrian_b 5y agoUnlike simple operations like a floating-point multiplication, trigonometric functions and the other transcedental functions are too complex, so they must be split into many steps. If a trigonometric function is encoded as a single instruction, then it must launch a microprogram, to execute the many required steps. A microprogram cannot execute faster than when the same execution steps would have been encoded as separate instructions, the only advantage of encoding a trigonometric function in a single instruction would be to reduce the program size. Most programs contain few trigonometric functions, so the reduction in program size is not worthwhile. While a microprogrammed trigonometric function could be as fast as the equivalent sequence of instructions, in reality it is usually much slower. The reason is that the modern CPUs are optimized for the most frequent instructions and they dedicate a minimum of resources for the seldom used microprogrammed instructions, so these hit various limitations that do not exist for the simple instructions. The microprogrammed instructions usually have some phases whose execution cannot be overlapped in time with other instructions, which leads to lower performance.
- monocasa 5y agoLast time I checked, x87 (and MMX which uses the same register file) aren't renamed. There's only the architectural register file. At that point these sorts of concerns don't matter because you're not metaprogramming rename hardware through the veneer of the architectural registers.
- brucedawson 5y agoIf x87 and MMX registers don't support renaming then that means that they can't support OOO and speculative execution of these instructions. This is possible but seems unlikely to me. That is, even though not a lot of x87/MMX code is executed these days (and even less is written) I would be surprised if the OOO/speculative-execution of the processor gets halted whenever these instructions are encountered. Note that x87 instructions still get used in most 32-bit programs because the x87 registers are the defined way for functions to return floating-point results. Which is to say, citation needed.
- monocasa 5y agoIt looks like my information was woefully out of date. They're fully renamed and executed OoO now, and on Intel even share a PRF with AVX-512 K registers interestingly enough. Thanks for keeping me honest.
- Dylan16807 5y ago> That is, even though not a lot of x87/MMX code is executed these days (and even less is written) I would be surprised if the OOO/speculative-execution of the processor gets halted whenever these instructions are encountered. The implications aren't that bad. Only instructions using the outputs of x87/MMX instructions would have to stall. And especially if you're only using them to shift values during function returns, then that wouldn't have to have a huge impact on performance.
- gpderetta 5y agoThe x87 register stack has been renamed on Intel desktop/server OoO cpus since the original Pentium Pro, by special handilng of the fxch instruction. If anything, AMD improved its handling on recent Zens. Take a look at Agner manuals. It is plausible it will be dropped at some point but we are not there yet. [1] newer atoms, although a
- TheMatten 5y agoAfter reading https://news.ycombinator.com/item?id=29104841 https://news.ycombinator.com/item?id=29104841 today and wondering why they zero one of registers using XOR on itself, it suddenly makes a lot of sense.
- commandlinefan 5y agoI asked why that was when I took assembler in college and the instructor didn't know (he said, "oh, um, it's faster"). I'm glad to finally find the actual correct answer to that question.
- monocasa 5y agoTo be fair, the choice of xor eax, eax is 'faster' than mov eax, 0. The xor option is only two bytes versus the five of mov, which can have I$ and decode width concerns. And back in the day, five bytes was pretty awkward for up to 32 bit data buses, where you'd need at least two cycles to even read the mov instruction. That's why existing code tended to use xor eax, eax in the first place, even before Intel started adding rename hardware to their processors.
- bonzini 5y agoSame for xor ax,ax on 16-bit processors by the way (2 bytes vs 3), and even earlier for the 8-bit 8080 (1 byte vs 2). The xor or sub trick dates to the late 70s.
- philh 5y agoAuthor in the old thread (https://news.ycombinator.com/item?id=19262249 https://news.ycombinator.com/item?id=19262249) says > An x86-64 CPU has sixteen integer registers, but 100-200 physical integer registers. Every time an instruction writes to, say, RAX the renamer chooses an available physical register and does the write to it, recording the fact that RAX is now physical-register #137. This allows the breaking of dependency chains, thus allowing execution parallelism. I'm curious why they have so many more physical registers than... logical? registers. I have a couple of guesses: * Physical registers are physically cheaper to add than logical registers. * Adding logical registers breaks backwards compatibility, or at best means you get no speedup on things written (/compiled) for fewer logical registers. Adding physical registers lets you improve performance without recompiling. * Adding logical registers increases complexity for people writing assembly and/or compilers. Adding physical registers moves that complexity to people designing CPUs. Are some of these correct? Other reasons I'm missing?
- kps 5y ago> Adding logical registers breaks backwards compatibility This, plus adding logical registers increases instruction size and therefore decreases the number of instructions that can be fetched with a given memory bandwidth.
- deleted 5y ago[deleted]
- msla 5y agoHaving more physical registers than logical means the CPU can do optimizations, opcodes don't have to be as big (it takes more bits to encode more registers), compatibility with older binary code is maintained, and CPUs at different price points can have different numbers of physical registers while all being able to run the same binaries. (I don't know if any manufacturer actually does that last thing, however.)
- brucedawson 5y agoAdding more logical registers is compatibility breaking. And, since you have to encode the register specifier in the instruction it means larger instructions (hence the compatibility breaking) which makes reading and decoding instructions slower. And, regardless of how many logical registers you have you need to have more physical registers. These are needed for out-of-order (OOO) execution and speculative execution. An OOO super-scalar speculative CPU can have hundreds of instructions in flight and these instructions all need physical registers to work on. If you don't have excess physical registers you can't do OOO or speculative execution.
- commandlinefan 5y ago> 1: add eax, 1 > 2: mov ebx, eax > 3: xor eax, eax > 4: add eax, ecx > Ideally we would like our awesome out-of-order processor to execute instructions 1 and 3 in parallel. There is a literal data dependency between them, but a sufficiently advanced processor could detect that this dependency is artificial. This doesn't seem right... instruction 2 does need to run after instruction 1 and before instruction 3.
- monocasa 5y agoNot if it's renamed. Instruction 2 does need to run after instruction 1, but instruction 3 doesn't need to run after them. xor eax, eax doesn't depend on the previous value of eax (because xoring anything at all with itself is zero), so you can assign it a new physical register file slot and just set it to zero.
- brucedawson 5y agoYep, exactly. The rule of thumb is that whenever a register is written to a new register mapping is created. For instruction #1 eax might be assigned to physical register 103. It maintains that identity for instruction #2. For instruction #3 eax can be assigned to, let's say, physical register 67. Then for instruction #4 it could be assigned physical register 92. The only tricky thing is that instruction #3 also reads from eax, which normally would cause a dependency, but because XOR doesn't depend on the register contents this clever optimization applies. Here's another variant: > 1: add eax, 1 > 2: mov ebx, eax > 3: mov eax, 42 > 4: add eax, ecx Here instruction 3 writes the constant 42 to the register. This might make it clearer why instruction #3 can run in parallel with or before instruction #1. Note that the results must be _retired_ in order, but execution can happen out of order.
- superjan 5y agoA plus for the benchmarks. One more complication of xor xx,xx goes unmentioned: xor also (partially) affects the flags register. That should get renamed as well.