[0] https://queue.acm.org/detail.cfm?id=3212479
Old discussion (skip the first reply for meat!)
[0] https://queue.acm.org/detail.cfm?id=3212479
Old discussion (skip the first reply for meat!)
Is there a reason speculative execution had to be added to the processor and not compilers? Would we have been better off if the true memory model was exposed to compilers?
Binary compatibility is the reason. You can't break the memory model or the instruction set unless you actually have the ability to re-compile everything that your users want to run. This almost never happens in the personal computing world, and emulation is only a partial solution. Intel managed to transition from the 8080 to the 8086 without backwards compatibility by preserving assembly language compatibility, but every architecture shift since then has required either emulation or a backwards-compatible processor mode.
I fail to see how having shared speculation is incompatible with existing binaries. AMD processors for example check security before speculating, instead of just running things in parallel and hoping they don't need to reset registers later.
Can you explain how speculative execution is systemic to x86? I can't see it to be honest.
Wasn't this supposed to be the lesson of Itanium and VLIW?
I have read (without understanding for myself) that VLIW it had two problems: (a) no one can actually write an appropriate compiler, and (b) that's not surprising because the non-literalness of assembly is a useful hinge point allowing CPU designers to evolve their implementations while retaining a stable interface.
But this thread is talking about going even further and exposing new CPU features to programmers, and not just compilers. Does anyone have thoughts about what abstractions they would like to see?
You could argue they already do, __builtin_prefetch and it's associated assembly instructions offer a way to manipulate the cache, but at the same time they seem to hedge their bets and pretend the cache isn't there. On the other end we've got programmers that know it's there and jump through hoops to try and keep things in contiguous memory, tricking the CPU into doing the correct thing.
That is truly an elephant in the room situation, everyone knows there is a magic black box there but do their best to ignore it because there is no other option.
> Wasn't this supposed to be the lesson of Itanium and VLIW?
It was the lesson, but that doesn't mean it was the correct one. In an alternate universe where there was no x86_64 we might have learned the opposite lesson.
I sometimes wish we wrote modern software for swarms of little 16-bit CPUs with 64kB of RAM and dealt with larger data structures by passing messages to other CPUs and to a DRAM-backed database.
And yes, a lot of software architecture is actually about emulating the above picture on a machine that pretends to have a flat address space, even though it actually only has 64kB of truly random-access memory.
Case in point, we have already seen a few times how unanticipated NUMA or thread scheduling weirdness can totally tank performance- and these are finer details!
The CPU knows what the software is actually doing and has done in the past. The compiler does not.
The CPU can change implementations; the compiler must bake any assumptions in at build time.
Speculative execution is required so long as memory latencies are so large. That trend isn’t reversing itself anytime soon.
profile guided optimizations?
At least profile guided optimization gives you a degree of control.
The compiler is poor at anticipating the many types of dynamic program behaviour. But the CPU is also bad at dealing with it. The very simple statistical model for branch prediction is used because space on the die is precious. Cycle accurate simulation is done routinely, if anyone had come up with a much better (and equally general) predictor that could fit on die it would already be in use. People have tried many variants of dynamically recompiling machine code. For example, early MIPS processors (R3000) recompiled everything to be slightly closer to the specific CPU, and exposed pipeline stages (like delayed branch, delayed load), software page fault handling etc.
In some ISAs you can even encode the branch prediction hints the compiler makes (from analysis or profiling) into the opcode. But the gains are limited.