Context is key. This list was originally written by Jeff Dean, as noted in the credits. He's a legendary [1] Google engineer who (usually side-by-side with the under-credited Sanjay Ghemawat) created such systems as MapReduce, GFS, Bigtable, and Spanner. I'd read "numbers every programmer should know" as "numbers every programmer who wants to be like Jeff Dean would benefit from learning about". I can see how folks might interpret it as gatekeeping—if you can't rattle off these numbers exactly from memory, you aren't a real programmer. I don't think that's the spirit in which it's intended.
These numbers are useful in designing efficient systems in Google datacenters, making decisions like "is it better for all of my stateless servers to keep this on local disk or to send an RPC to an in-cluster server that has it in RAM?" or "do I have sufficient deadline to fail over to another cluster in the 99.999%ile case?" You don't really need to have the entire list memorized, but it helps to know the appropriate operations to consider when deciding if something is suitable from efficiency and latency standpoints. It helps to have intuition for the orders of magnitude. It's okay to look at the cheat sheet. You can do math on these numbers to get a surprisingly accurate idea of a design's performance/feasibility long before you work up a prototype, moving more quickly. Comparing these expectations to experiment helps you spot implementation flaws and get closer to achieving the theoretical performance. And if nothing else, being familiar with the ideas here will help you ace a Google system design interview.
Many of these numbers are also useful in other contexts in which performance matters and you have fairly direct control over what expensive operations are performed. (They're less useful say in an interpreted programming language in which much happens behind your back and you may not care much about performance anyway.) It certainly wouldn't hurt any programmer to know these numbers.
[1] see the "Jeff Dean facts" for an idea of how Google engineers view him: https://www.informatika.bg/jeffdean
https://stackoverflow.com/questions/11227809/why-is-processi...
There‘s no way from conclude from „memory latency“ to „branch prediction“.
Maybe the list should link the SO answer and similar descriptions of macro-manifestations of seemingly nano-things!
If you mean the whole range of values I would agree.
However, once I've interviewed a developer for a front-end position that was entirely oblivious to the cost of making an HTTP request, firmly believing that only large downloads had any measurable impact on performance.
Even if you do not have to do back-of-the-napkin calculations on cache latency, knowing the relative cost of each of these operations does wonders to your decision process.
For a lot of programming, the lowest of these latencies - cache and memory access and branch mispredicts are averaged out and essentially unnoticeable or not worth caring too much about. Which is to be expected, being the design goal. But it's not too rare, even in regular, high-level language programming for them to become 'macroscopic' and that is a useful, practical thing to be aware of, rather than some academic curiosity.
Let's say you have code that is 50% main memory and 50% L1 cache. Let's say there are 1000 operations.
You have (500 x 100ns) + (500 x 1ns) or 50500ns total time.
Now let's say you optimize the code so that they are all L3 operations: 20ns x 1000 operations is 20000ns, or over twice as fast.
Edit: to clarify a bit further - people read this list and think '1 nanosecond, 5 nanoseconds, not important to me, academic, etc'. My point is that's a misunderstanding but the list alone doesn't do a good job of disabusing one of the misunderstanding.
1. Cache-oblivious data-structures work no matter the cache-size. They aren't 100% optimized to a particular cache size, but they are proven to be "efficient" across many different cache sizes.
2. For an example of #1, matrix multiplication is one of the most optimized functions of modern computing. One major step is to transpose the matrix, so that the "vertical" movement turns into a "horizontal" movement. The horizontal movement, which is "address + 1" allows for the cache to work with your loop, while a vertical movement across the matrix is "address + width", and not cache-efficient.
------------
3. I think it is safe to start making some degree of cache-assumptions on modern systems. All caches today have 64-byte cache lines (or greater). That's ARM, Intel, and AMD CPUs all use 64-byte cache lines. AMD GCN GPUs use 64-byte cache lines. AMD RDNA GPUs use 128-byte cache lines (and Intel is rumored to "pair up" cache lines into 128-byte segments at L3).
This means that every memory operation to main-memory reads or writes at least 64-bytes of data at a time, on all modern systems. DDR4 RAM itself has Burst-Length 8, meaning 64-bytes is literally the smallest unit of data that can be addressed, so it isn't surprising that all CPUs / GPUs are working with such a large number these days.
-----------
Given the 64-byte cache line (or 128-byte, for Intel's L3 cache or AMD RDNA GPUs), "unrolling" your linked lists, or using B-trees of larger sizes (instead of the cache-inefficient binary tree), or d-heaps (instead of binary heaps) plays to the benefit of caches helps out a lot.
You don't need to know the specific size of the cache line to know that d-heaps are more efficient than binary-heaps on modern, cache-heavy systems. You just need to know that sequential-memory access stays within the L1 cache.
The latency numbers are still useful for making back of the envelope calculations about how much it is possible to gain and whether it's worth the trouble.
You can grossly mitigate the random-memory read/write by using unrolled linked lists (https://en.wikipedia.org/wiki/Unrolled_linked_list).
Or, instead of using binary-heaps, use d-heaps: https://en.wikipedia.org/wiki/D-ary_heap
Sometimes the source data structure is just a bad fit for the expressed data. I've seen this often in combinatorial expansions of data stored in hierarchal structure to a 2d table.
Sometimes all, or a majority, of the data is going to be touched anyway so it's more costly to avoid a full read and instead more optimal to make the results of that read parallel; either by sorting to a more suitable structure or by working on streams / permutations / parallel processes from a dispatch design.
RAM:SSD:Magnetic - 100:10:1
For example 10 terabytes of RAM costs as much as 1 petabyte of spinning disk magnetic storage.
I'm kind of in an odd niche, so maybe your last sentiment applies. But it strikes me as very un-hacker-mindset to be like, "no one _needs_ to know X". it's curiosity.
there are also domains like realtime control systems, video conferencing, image processing, devices where battery life is at a premium, ai, video editing, decoding, encoding, stock market trading.....
when I'm doing optimisation, my goal is not to explicit say "oh, L1 cache is X nanoseconds", it's to say "the profile pattern of the performance we see indicates that X is the current performance bottleneck, and if we can change programming/design, we'll gain Y". Especially since, by the time we're optimising heavily, it generally assumes something was amiss in your original coding or expectations of the system anyway, so your memorised numbers of what performance should be are often shown to be wrong in the first place, and your real world measurements will tell you the numbers that are relevant to your particular use case.
And it is always better not to need to optimize, because the code is already fast enough. You may leap up to proclaim against premature optimization, but only if you have forgotten what Knuth actually wrote.
There is lots of programming that is not performance-sensitive, but I don't do it, or hire for it.