It is still very possible to hand write assembly that’s faster than optimized C. The compiler can’t just magically assume the same pre/post conditions that you can. Also register allocation is an NP problem (graph coloring).
Can a human keep that level of optimization up for the length of an OS? No idea. Would it even be worth it? Probably not.
Having inspected compiler output for many years, I very much disagree. They've gotten a little better, but I still see plenty of unnecessary data shuffling and other things no sane human Asm programmer would ever do. A human has a far better understanding of what he/she wants the program to do, and this also includes things like which variables should go in registers, and when. A compiler can analyse dataflow and somewhat understand, but especially when external functions are involved, the knowledge is still incomplete.
Compilers are a lot better at deciding which of two equivalent instruction sequences will be faster on a given CPU architecture.
This ironically makes them worse overall, since (when told to) they always attempt to optimise for speed, even at the expense of size. They don't know that making one part of the code smaller, although somewhat slower, could allow another more critical part to fit completely into cache and improve performance overall.
Speaking as a long-time Asm programmer myself, rarely do we act like HLL compilers and literally transcribe statement-for-statement what the HLL (e.g. C or C++) source code does. We start with a high-level understanding of the algorithm; maybe pseudocode, maybe flowcharts, maybe something derived from HLL source. Then we think at the register and instruction level and decide how to implement it. It's a somewhat creative process, unlike the literal pattern-matching compilers do. Example: https://news.ycombinator.com/item?id=15423674
OK, but in the general case, at scale, in a complex program, compilers are better able to make register allocation decisions than humans. Some compilers may not be as good as others, and I would love to see some examples, but it has been a long time since this debate was settled.
"A compiler can analyse dataflow and somewhat understand, but especially when external functions are involved, the knowledge is still incomplete."
https://en.wikipedia.org/wiki/Interprocedural_analysis
Sure, dynamic linking throws a wrench into things, but equally true for humans who may not understand how the function they are calling really works.
"making one part of the code smaller, although somewhat slower, could allow another more critical part to fit completely into cache and improve performance overall."
I am sure it happens. It is far more common for that sort of optimization to be irrelevant due to the size and complexity of the system. Compilers are also capable of optimizing for code size, it is similar to optimizing for speed but not important in most cases.
You keep repeating that assumption, but offer zero proof or even any argument really to why it might be true even.
Anyway, a few weeks ago, I picked up one of the projects I worked in the early 90s, a mildly math heavy program that dealt with a pair of functions that I thought could easily be vectorized and oh! The Pentiums of today have vector instructions, and in reading over the programming manual, yes, it looked like it would be straightforward to write some vector code, in assembly (something I haven't done---should be fun).
My code easily beat clang (using C code) on its highest optimization setting. Easily beat, but it got pretty close. GCC? It (again, using the C version of the code) was smoking my code at -O2 by an embarrassing amount. And it wasn't even using the vectorized instructions! And to make sure, I ran the code on several different systems, so I don't think it was this version of the Pentium having a bad MMX implementation over that Pentium.
I haven't taken the time to figure out what GCC did (perhaps it knows better how to schedule MMX code than I do) as I was doing this for fun, not profit. All the data I was working with was in registers in my code.
Compilers can definitely do better than humans in some situations, but you can also use an optimizing assembler that helps with register allocation (not as important for x64) and all sorts of peephole optimizations. The compiler doesn't reason about the data, which means the compiler has to make worst-case assumptions that the programmer knows won't occur. This is a big source of inefficiency. Compilers will never be able to make strategic optimizations because the compiler doesn't understand what it's compiling.
Like with chess, computer + human is the winning combination.
"The compiler doesn't reason about the data"
It certainly can; it is hard in a language like C due to the deficiencies of the type system, but in other languages the programmer can more explicitly state constraints on values. Compilers can also make useful inferences about constraints based on e.g. conditional statements, loop bounds, etc.
Where humans win is in designing more efficient algorithms. Compilers are great at reducing the constants but still cannot improve the asymptotics. In other words, human effort is better spent on the higher-level concerns, and yes, some languages are better than others for that task.
"Like with chess, computer + human is the winning combination."
Chess-playing programs are so much better than humans that it is not even interesting to compare them anymore.
The reality is that the fastest compilers for the fastest languages still require babysitting and hand written ASM for peak performance.
> human effort is better spent on the higher-level concerns
For most cases, yes I (and likely most people) agree. Although there are times when we do care and it is important to know that our tools are not infallible.
Curious what you mean here. My layman's observation is that computers have far surpassed humans in that space.
No, this strongly depends on the form of the program. SSA form permits polynomial solution of register allocation. (SSA is the most widely used form in compilers; LLVM, GCC and even MSVC use it, and many more.)
OTOH, GP only actually said "NP problem", of which P is a subset, so they're not wrong. :)