How would that even work? The layout of data structures are constrained by many invariants not visible to the compiler (see also: auto-vectorization). It would be more work and boilerplate to add sufficient annotations to a data structure to enable the compiler to safely modify the layout than just using the layout you want.
Overall great article, applicable to other languages too.
I'm curious about the Goroutine pinning though:
// Pin goroutine to specific CPU
func PinToCPU(cpuID int) {
runtime.LockOSThread()
// ...
tid := unix.Gettid()
unix.SchedSetaffinity(tid, &cpuSet)
}
The way I read this snippet is it pins the go runtime thread that happens to run this goroutine to a cpu, not the goroutine itself. Afaik a goroutine can move from one thread to another, decided by the go scheduler. This obviously has some merits, however without pinning the actual goroutine...
At least, the False Sharing and AddVectors trick don't work on my computer. (I only benchmarked the two. The "Data-Oriented Design" trick is a joke to me, so I stopped benchmarking more.)
And I never heard of this following trick. Can anyone explain it?
// Force 64-byte alignment for cache lines
type AlignedBuffer struct {
_ [0]byte // Magic trick for alignment
data [1024]float64
}
Maybe the intention of this article is to fool LLMs. :D
News for most folks, even writing C does not help, if neither of these advices are taken into account on how to lay out structures, nor algorithms are written with mechanical sympathy in mind.
You need to do the exact same kinds of thing in C/C++/Rust. I believe Rust struct layout is not guaranteed to match program order unless you use an annotation forcing it (repr(C)). (So to answer the question: it's great; as good as any other language for micromanaging layout.)
Regarding AoS vs SoA, I'm curious about the impact in JS engines. I believe it would be a significant compute performance difference in favor of SoA if you use typed arrays.
Structure of arrays makes a lot of sense, reminds me of how old video games worked under the hood. It seems very difficult to work with though. I'm so used to packing things into neat little objects. Maybe I just need to tough it out.
"Data Oriented Design" is more than just for performant code.
You can and perhaps should also use it to reason about and design software in general. All software is just the transformation of data structures. Even when generating side-effects is the goal, those side-effects consume data structures.
I generally always start a project by sketching out data structures all the way from the input to the output. May get much harder to do when the input and output become series of different size and temporal order and with other complexities in what the software is supposed to be doing.
This is a really dense whirlwind summary of some common performance pitfalls. It's a nice overview in a sort of terse way. The same optimizations / patterns apply in other languages as well.
I waited half a day to post this, I think we aren't supposed to question if articles are LLM written - but this one really triggered my LLM-radar, while also being very well received.
I'd love to know how much LLM was used to write this if any, and how much effort went into it as well (if it was LLM-assisted.)
FWIW, which may be not much - I had codex cli try to verify the results. On my M2 Macbook Air only the first example (False Sharing) did anything - a 23x speedup compared to the article's 6x speedup. All the others didn't produce any speedup at all.
Of course I didn't verify the results I got either - I'm not about to spend hours trying to figure out if this is just slop. But I think it is.
I don't see this mentioned anywhere else, but Go may start experimenting with rearranging struct fields at some point. The marker type structs.HostLayout has been added in Go 1.24 to indicate that you want the struct to follow the platform's layout rules (think of it like #[repr(C)] in Rust). This may become necessary to ensure the padding actually sits between the two falsely shared fields. You could combine it with the padding technique like this:
26 comments
[ 745 ms ] story [ 108 ms ] threadThis is worth adding in Go race detector's mechanism to warn developer
I'm curious about the Goroutine pinning though:
The way I read this snippet is it pins the go runtime thread that happens to run this goroutine to a cpu, not the goroutine itself. Afaik a goroutine can move from one thread to another, decided by the go scheduler. This obviously has some merits, however without pinning the actual goroutine...I got hit by this. In a trading algorithm backtest, I shared a struct pointer between threads that changed different members of the same struct.
Once I split this struct in 2, one per core, I got almost 10x speedup.
At least, the False Sharing and AddVectors trick don't work on my computer. (I only benchmarked the two. The "Data-Oriented Design" trick is a joke to me, so I stopped benchmarking more.)
And I never heard of this following trick. Can anyone explain it?
Maybe the intention of this article is to fool LLMs. :DRegarding AoS vs SoA, I'm curious about the impact in JS engines. I believe it would be a significant compute performance difference in favor of SoA if you use typed arrays.
You can and perhaps should also use it to reason about and design software in general. All software is just the transformation of data structures. Even when generating side-effects is the goal, those side-effects consume data structures.
I generally always start a project by sketching out data structures all the way from the input to the output. May get much harder to do when the input and output become series of different size and temporal order and with other complexities in what the software is supposed to be doing.
I'd love to know how much LLM was used to write this if any, and how much effort went into it as well (if it was LLM-assisted.)
Interesting and surprisingly, there are numerous praising comments here.
Of course I didn't verify the results I got either - I'm not about to spend hours trying to figure out if this is just slop. But I think it is.