I used to write a lot of assembler back in the day. It is not just that compilers have gotten better, it is that most hardware now has lots of logic devoted to out-of-order execution of code. The real micro-issue is scheduling, most computer instructions take several machine clocks to produce a result -and a memory load that misses cache may take several hundred! So the idea was to schedule other instructions to do something useful, instead of waiting for a result. And modern machines can issue several instructions per clock period. Once we started getting out-of-order execution HW, I found that trying to get great performance with hand coding became a mugs game. First the out-of-order HW would not execute the instructions in your carefully crafted order, the fancy new HW architecture has reduced the penalty of inoptimal software scheduling enough that the compiler was usually within a few percent of your performance. Also I found out that compilers were now implementing wellknown but complexity generating tricks, such as unrolling, bottom loading, software pipelining etc. The bottom line, you have to work really really hard, skip some of these tricks and the compiler beats you. Use them all and the number of assembler instructions you need increase several fold!
Probably even more important, most performance issues, are not about instruction issue rates, but getting the data into the CPU. As I mentioned above, memory latency is now hundreds of cycles, and the CPU can execute several instructions per clock period, so
unless the program -and especially the data structures are designed so that the cache hit rate is exceedingly high, microtuning at the instruction level will have no payoff. Just like
military types say amateurs talk tactics, pros talk logistics. Performance programming is now more than 90% logistics (moving data). And this is hard to quantify, as modern memory management typically has multiple levels of cache, and virtual memory pages are handled by a hardware unit called the TLB. Also lowlevel alignment of addresses becomes important, as actual data
transfers, are not in units of bytes, or even 64bit long-longs, but they come in units
of cache lines. Then most modern machines have hardware that tries to predict which cache line misses you might need in the near future and issue automatic prefetches to get them into the cache. So the reality
is that with modern CPUs performance models are so complex as to be almost un-understandable. Even detailed hardware simulators can never match the exact logic of the chips, so exact
tuning is simply impossible anymore.
There is still a place for some hand coding. Math libraries (like say the exp function), as are the more important linear algebra operations (like matrix multiply) are still usually hand coded
by experts that work for the hardware vendor (i.e. Intel or AMD, or IBM), but they probably
only need a couple of top notch assembler programmers per mega-computer corp.