The idea there is that on i386 doing memory copy by explicit optimized assembly loop is faster than using CPU instruction that does that. And then there are i386 implementations that can do that faster (eg. 80386 itself) that are deprecated by that.
I'm doubtful that using anything other than REP MOVS for copying was ever slow enough to require making a decision on. Perhaps for extremely tiny copies, but I do wonder if the original decision was based on skewed microbenchmarking.
REP MOVS had a horrifyingly high setup penalty for some Intel CPUs, to the point where an ordinary function call to a simple unoptimized c copy implementation would beat it on sizes shorter than kilobytes. IIRC the first P4 cpus (Northwood) were especially bad, with the implementation steadily improving from there.
At first, REP MOVS and REP STOS were obviously better. Shorter code, that couldn't possibly be beaten by unrolled loops of anything because of instruction decoding overhead on each instruction cycle.
Then for a range of x86 generations maybe starting with the Pentium Pro (first OOO) or Pentium (first superscalar), REP MOVS and REP STOS called into microcode and it was significantly faster to use unrolled loops of register-sized operations that could execute with some level of instruction parallelism, at least if the addresses had good alignment, and this advantage increased if MMX and later SSE and AVX registers were used.
The speed difference was particularly important for known big, aligned copies such as page copies and page clearing. If the copy was not well aligned, though, there needed to be shift operations or unaligned memory accesses, and that slowed the unrolled loops.
Later, microcode for REP MOVS and REP STOS was improved to make using them consistently worth using again, as was the overhead of performing an instruction that calls that kind of microcode.
They had access to cache-line actions not available to regular code. In particlar they could write a whole cache line putting it into the M state (from MESI) without needing to fetch the cache line from RAM or higher level cache first, and REP MOVS had access to good prefetching patterns on the read side. Whereas a memory-write instruction like MOV MEM,REG needed to fetch the cache line first, to modify a single word in it. Those cache line improvements reduced the amount of memory bus traffic, which directly improved performance. Also, the CPU could determine its ideal internal alignments for those operations quickly, instead of the setup needed with MMX/SSE/AVX to align to cache. Also, no shift instructions or unaligned accesses required. Also, in principle REP MOVS and REP STOS could tell the cache to use different prefetch and eviction policies, though I'm not sure if this was used. Finally, just using the very short instructions REP MOVS and REP STOS, compared with the unrolled code and extra stuff to deal with alignment, reduced instruction cache pressure.
So at first REP MOVS and REP STOS could not be beaten, and more recently (as of many years now) they are fast again on OOO machines, but there was a time in the middle where they were easily beaten as certain unrolled loops had better mechanical sympathy with the CPU's memory system than the not particularly optimised microcode of that time period.
11 comments
[ 3.0 ms ] story [ 33.1 ms ] threadPrevious HN discussion: https://news.ycombinator.com/item?id=17845252
Then for a range of x86 generations maybe starting with the Pentium Pro (first OOO) or Pentium (first superscalar), REP MOVS and REP STOS called into microcode and it was significantly faster to use unrolled loops of register-sized operations that could execute with some level of instruction parallelism, at least if the addresses had good alignment, and this advantage increased if MMX and later SSE and AVX registers were used.
The speed difference was particularly important for known big, aligned copies such as page copies and page clearing. If the copy was not well aligned, though, there needed to be shift operations or unaligned memory accesses, and that slowed the unrolled loops.
Later, microcode for REP MOVS and REP STOS was improved to make using them consistently worth using again, as was the overhead of performing an instruction that calls that kind of microcode.
They had access to cache-line actions not available to regular code. In particlar they could write a whole cache line putting it into the M state (from MESI) without needing to fetch the cache line from RAM or higher level cache first, and REP MOVS had access to good prefetching patterns on the read side. Whereas a memory-write instruction like MOV MEM,REG needed to fetch the cache line first, to modify a single word in it. Those cache line improvements reduced the amount of memory bus traffic, which directly improved performance. Also, the CPU could determine its ideal internal alignments for those operations quickly, instead of the setup needed with MMX/SSE/AVX to align to cache. Also, no shift instructions or unaligned accesses required. Also, in principle REP MOVS and REP STOS could tell the cache to use different prefetch and eviction policies, though I'm not sure if this was used. Finally, just using the very short instructions REP MOVS and REP STOS, compared with the unrolled code and extra stuff to deal with alignment, reduced instruction cache pressure.
So at first REP MOVS and REP STOS could not be beaten, and more recently (as of many years now) they are fast again on OOO machines, but there was a time in the middle where they were easily beaten as certain unrolled loops had better mechanical sympathy with the CPU's memory system than the not particularly optimised microcode of that time period.