Much of the difference in optimizability is that C/C++ allows you to do fairly extreme copy elision. This is important because memory bandwidth tends to be a major performance bottleneck.

C/C++ code can trivially reach into some remote bit of state and mutate it directly. The compiler rarely objects, no matter how ugly the access method or pattern. It is up to the programmer to make sure the invariants hold and the compiler can’t always see through it. These elided copies add up, though it usually has a maintainability and safety cost.

It is related to why functional languages tend to be mediocre for supercomputing in practice despite having nice properties for massive parallelism in theory.

Clojure creator Rich Hickey describes traditional imperative programming as "PLace Oriented Programming (PLOP)" when talking about how Clojure's approach is better for certain kinds of problems.

I agree with his point, and Clojure is my favorite programming language, but the computer itself is place-oriented. It runs on reading from and writing to locations in memory, and extracting maximum efficiency from it requires direct control over that memory.

Modern computers are exquisitely sensitive to spatial and temporal locality because many of the hardware features that speed your code up (for free!) take advantage of data or instructions that are “next to each other.” This can be a challenge in a functional program, because not only do you often have little control about where things go, the abstractions that make cool things possible (eg, closures, or lazy evaluation) naturally introduce indirection that hurts locality.

> but the computer itself is place-oriented

You could similarly argue that the computer is in fact fundamentally functional, since the core instruction cycle (Fetch Decode Execute) does not modify any memory.

The key insight into that system is that we've embedded a functional system within a mechanism that makes it imperative. The functional cycle can output commands to fetch it more data, that it can then use in the next cycle. It becomes imperative by the addition of time, which it needs because it's limited in the amount of input and output.

If a function is computed in the woods, does it return a result?

Only if it has a place to put the result.

That's a nice explanation, thanks.

Are there any common resources for this kind of access methods and patterns that specifically aim to bridge the soft-/hardware gap? The best one I can come up with is Performance Analysis and Tuning on Modern CPUs[0] by Denis Bakhvalov.

[0]: https://github.com/dendibakh/perf-book

I recently came across https://www.kernel.org/pub/linux/kernel/people/paulmck/perfb... , it draws from author's experience about applying optimizations in linux codebase .

I also like this very readable paper https://xnning.github.io/papers/perceus.pdf, about reducing the costs of reference counting in context of functional languages.