2 ms·
Dijkstra writes about subroutine calling: > We should recognise the closed subroutines as one of the greatest software inventions; it has survived three genera
by kragen 1y ago
Dijkstra writes about subroutine calling:
> We should recognise the closed subroutines as one of the greatest software inventions; it has survived three generations of computers and it will survive a few more, because it caters for the implementation of one of our basic patterns of abstraction. Regrettably enough, its importance has been underestimated in the design of the third generation computers, in which the great number of explicitly named registers of the arithmetic unit implies a large overhead on the subroutine mechanism.
Presumably what he's saying is that if you have 16 architectural registers instead of four, entering a subroutine involves saving 16 registers, which takes four times as long as saving four registers, which discourages factoring your program into small subroutines. Is there a more reasonable interpretation?
Because I think this interpretation is wrong. If your leaf subroutine requires six registers for its work, and you only have four caller-saved registers, you need to save the values of two callee-saved registers on entry and restore them on exit. And that's true whether your architecture has eight registers, or 16, or 32. Similarly, if a value needs to survive across a subroutine call, you need to use a callee-saved register for it, or store it in memory.
So the cost of invoking a subroutine doesn't depend on the number of architectural registers, I think.
The exception is that, if you don't have enough registers, you need to store variables in memory, and variables in memory don't need to be saved and restored. You could even posit cases where moving variables from registers into memory makes your program faster because you don't have to spend time saving and restoring a register. (Probably not on a single-issue RISC, because you need extra loads and stores, but you could imagine machines whose memory access is fast enough for this.)
But, if that's the case for a particular variable, nothing stops you from using memory for it, perhaps occasionally holding its value in a caller-saved register temporarily. This saves you the cost of saving and restoring the register you could have used for it. It's as if the register doesn't exist.
So I'm skeptical of the thesis that large architectural register files make subroutine calls slower. At most, they fail to speed up subroutine calling by as much as they speed up other parts of your code.
On the other hand, I don't think I know anything relevant that Dijkstra didn't know in 01972. So, am I missing something?
(All this is assuming a constant number of instructions per second, although in fact stack architectures like the MuP21 might be able to hit a higher clock speed than more conventional designs.)
Large register files do slow down preemptive context switching (multithreading), because the operating system must save even registers that the problem-state code is not using, even caller-saved registers. And large sets of callee-saved registers also slow down cooperative context switching.