4 ms·
A problem is that on Sandy Bridge+, these complex instructions can't decode in the same cycle as simple instructions, so using them can reduce instruction decod
by brigade 9y ago
A problem is that on Sandy Bridge+, these complex instructions can't decode in the same cycle as simple instructions, so using them can reduce instruction decode throughput.
- BeeOnRope 9y agoIt depends what you mean exactly by "complex" instructions, but on SB+ and also on earlier architectures, the type of instructions discussed here (simple memory source or RMW instructions) decode just fine in parallel with other instructions. In general, load-op or RMW is strictly better than explicit load, op, store sequences when the source supports it and pretty much every decent x86 compiler will emit those. The "complex" instructions that have decode limitations are entirely different things like rep movs instructions, cpuid, integer division, etc, etc. Basically instructions that are implemented in micro-code.
- brigade 9y agoIgnoring microcode, the instruction decoders can, per cycle, decode one of three patterns: 4 instructions that decode to a single µop 1 instruction that decodes to 2 µops and 2 instructions that decode to a single µop each 1 instruction that decodes to 3 µops 1 instruction that decodes to 4 µops Obviously, RMW decodes to three unfused µops and thus the cycle it’s decoded there are only 3 µops emitted from the decoders of a potential 4.
- BeeOnRope 9y ago> Obviously, RMW decodes to three unfused µops It's not obvious at all, in fact it's wrong. Almost all RMWs, including inc, decode to 4 unfused uops (1 load, 1 ALU, 2 store). More importantly, the numbers you quote above for uop decode patterns are for fused uops, not unfused (after all, the decoders produce fused ops). In the fused domain 2 and 3 uops are common (inc is 3 but add is 2, not sure they differ - you could just use add [mem], 1 to work around this oddity). So in many cases (2 uops) it will decode in parallel with other instructions. More to the point: 1) This applies only to the legacy decode: most instructions in most applications, and nearly all in small loops like this example will code from the uop cache, where the restrictions above don't apply. 2) Even if for some reason you are running this entirely on the legacy decoders, 3 fused uops per cycle is still "fine" in comparison to the simple instruction variant because the latter will need 4 uops to accomplish the same thing, so the reduction of apparent decode throughput was canceled out by the fact that few total uops need to be decoded. 3) The only case above where more complex instructions actually results in fewer uops is the 3 uop case: the most complex case of 4 and the case of 2-1-1 still result in the max of 4 uops: so it's kind of the exception rather than the rule (if it is even correct on modern chips: if you got this from Agner that section is wrong for Skylake where the legacy encoder has improved but it's not reflected in the document). 4) The more interesting case of "complex" fused instructions is probably load-op, which only generate 1 (fused) uop so are eligible for the fast 1-1-1-1 decode treatment. There is pretty much no downside[1] to using "complex" memory destination RMW and memory source load-op instructions in modern x86, and all decent compilers generate them when they can. --- [1] Here I'm comparing using them vs not-using them in x86, which already has them - I'm explicitly not trying to compare whether it is good to put them in your ISA/arch in the first place.