A computer is a fractally complex dragon:
- A modern CPU resembles more of a fractally complex hardware-accelerated interpreter / virtual machine of an ossified standardized highish-level bytecode language (assembly operating on the architectural state),
- that was generated from a different highish-level bytecode language in a fractally complex manner (e.g. from llvm bytecode with optimization and lots of potential undefined behavior)
- that was generated from / interacts with a high-level human-written language in a fractally complex way (e.g. julia or C or C++).
I would say that humility in the face of dragons is the most important thing: You never stop learning, especially since all of this is a moving target.
To give a concrete example: It’s OK if new people don’t know that or why linked lists are bad in most contexts. It’s not OK if new people “know” that linked lists are performant because they were taught from books that adhere to obsolete paradigms (linked lists were OK in the 90s when e.g. java was developed, but since then compute has scaled much faster than memory, especially wrt latency).
To teach that humility I’d look at a small handful of dragons in some level of detail.
The Spectre attack (as opposed to the very different meltdown, which is an intel-specific fixed CPU-bug) is pretty good for (1), since it teaches about cache and locality, as well as superscalar stuff, latency hiding, speculative execution and the fine distinction between machine state and architectural state.
For (2) and (3), the concept of undefined behavior / assumptions is often overlooked and quite teachable. For that. I’d recommend reading a little of LLVM langref and playing around with UB-triggered “miscompiles”.
Fun exercise: Write a julia or C function that ends with
return x >= 0 ? x : 0 and happens to return a negative number in the REPL or in small C programs. Obviously you need to do bad stuff ™, involving pointers or integer overflows or race conditions or llvmcall, and this probably wont work with
Then meditate about how that reflects both the high level (the wording of the language spec for C/C++, or the code in julia that emits the broken assumptions into the llvm-IR) and intermediate representation (how llvm works with that, and the fiddling around to coax the optimizer into actually using the broken assumptions).