I think more engineers are becoming skeptical of optimizing compilers with that sort of implicit, potential "spooky action at a distance" where nonlocal code can cause performance regressions.
In Haskell, laziness and iterator fusion can do the same thing. Nonlocal code can cause local expressions to compile differently, resulting in - from an engineer's POV - nondeterministic performance, memory usage, GC pressure. "Space leaks" can also occur, again, due to nonlocal effects.
In JavaScript, the JIT can similarly cause local regressions due to nonlocal code. A single expression deep in a call stack mutating an object can result in it no longer fitting an optimized "shape", resulting in function deoptimization.
Just say no to spooky action at a distance in programming languages, in my opinion. It leads to tremendously difficult to debug regressions.
Because it's not the case with Rust or any compiled language? LLVM has shown regressions wich is what Rust uses to compile code.
2sec search on google: https://github.com/rust-lang/rust/issues/24194
I think there may be some edge cases (constant folding, dead code removal) of course, but a change to LLVM cannot cause the 3 sorts of changes I identified in Go, Haskell, and JavaScript.
I thought heap allocations are only triggered by taking pointers, i.e. using reference semantics (including casting to interface and calling a method whose receiver is by-ref)? If I'm careful to only use variables by-value (for types that can afford it, i.e. excluding map/array), what else can trigger heap allocation?
package main
import "fmt"
func main() {
closure := make_closure()
fmt.Println("closure():", closure())
}
func make_closure() func() int {
x := 1
return func() int { return x }
}
This prints: $ go run main.go
closure(): 1
If x were allocated on the stack, it would get nuked after we returned from make_closure(). In Rust you could move x to the closure, but I think in this Go example x would be heap allocated, assuming the Go compiler doesn't notice it can inline all of this and avoid allocating. Maybe assume a more complex example with a struct that had to be computed via a function argument or something :)
> There are millions of Users in each cache. There are tens of millions of Read States in each cache. There are hundreds of thousands of cache updates per second.
This is something that is probably never going to be hit locally in the development toolchain. You can certainly prefer the Rust memory system to Go and have that be a valid reason to use Rust over Go for something like dev toolchains, but you're not going to hit scale problems like those mentioned in the article.