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 -O0
.
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).
PS: the above was for people who want to do julia / C / C++. For people who want to do javascript stuff, you don’t need to look at llvm, but need to instead study internals of at least one JS engine (at least enough to understand where the dragons lie). For java people, you need to study JVM internals, here I recommend JVM Anatomy Quarks.