This reminds me of a portion of a talk Jonathan Blow gave[1], where he justifies this from a productivity angle. He explains how his initial implementation for virtually everything in Braid used arrays of records, and only after finding bottlenecks did he make changes, because if he had approached every technical challenge by trying to find the optimal data structure and algorithm he would never have shipped.
"There's a third thing [beyond speed and memory] that you might want to optimize for which is much more important than either of these, which is years of your life required per program implementation." This is of course from the perspective of a solo indie game developer, but it's a good and interesting perspective to consider.
The funny thing is even data structure is really an array of memory depending how low down deep one looks. All the people who took an intro computer science course knows, those who had to implement a heap allocator, it's all really just arrays underneath...
It's also notable that video games are programs that run for hours and iterate over large sets of very similar entities at 60 frames or more per second repeatedly and often do very similar operations on each of the entities.
That also means that "just do an array of flat records" is a very sane default even if it seems brutish at first.
I think when he said "just do an array of flat records" he meant as opposed to record of arrays (i.e. row oriented vs column oriented), as opposed to fancy data structures which I think you're assuming he was implying. Separate arrays for each data member are common in game engines exactly because they're good for iterating over, which as you said is common.
That's also great for letting the compiler unlock auto-vectorization opportunities without delving into the world of manual SIMD.
Even storing something simple such as an array of complex numbers as a structure of arrays (SoA) rather than an array of structures (AoS) can unlock a lot of optimizations. For example, less permutes/shuffles and more arithmetic instructions.
Depending on how many fields you actually need when you iterate over the data, you prevent cache pollution as well.
Jonathan Blow's own unreleased Jai programming language has a feature to make it trivial to switch between array-of-structs and struct-of-arrays.
From a quick search, it seems HackerNews's own jcelerier has put together a C++ library for doing this. https://github.com/celtera/ahsohtoa
I haven't watched his videos on his language for ages, but this was a big thing he wanted in his language: being able to swap between array-of-structs and struct-of-array quickly and easily.
Jonathan blow has spent the past 10 years and almost all of his money trying to build the perfect programming language for his 3rd game.
Also, Rule 5 contradicts the idea you quoted from Blow.
It's easy to see this outside of the perspective of a solo game developer. We all have deadlines to manage even in the regular 'corporate world'. And engineering time spent on a problem also translates to an actual cost.
It's a good consideration tbh.
I'd be careful extending learnings from games (solo or team efforts) to general programming as the needs and intent seem to be so different. We rarely see much code re-use in games outside of core, special-purpose buy largely isolated components and assets. Outside of games there's a much bigger emphasis on the data IME, and performance is often a nice-to-have.
It's been mixed moving to normal code: I haven't had to low-level optimise for ages now (man I miss that). But performance in the O() sense has been the same.
Game engine development is very much about processing of data. The pipeline is long and the tree is wide. Being able to reason about complicated data processing topologies mapped very easily across.
Yeah - A game is (generally) a one and done enterprise. Like a start up it's all about getting it out the door, there's little to no expectation of having to maintain it for any real length of time.
HN has some hot takes but that one is particularly impressive.
Fortnite, Roblox, League of Legends, Counter Strike . . . all very short term projects.
For every game like that, there are a thousand games that shipped, maybe got a few updates, and then they were done.
We can probably add to that the fact that the nominated titles almost certainly started out with spaghetti code that had to be refactored, reworked, and gave maintainers nightmares in their sleep
[dead]
> how his initial implementation for virtually everything in Braid used arrays of records
This is me with hash maps.
This is slightly surprising to me, since Braid is kinda infamous for being in development for a pretty long time for a puzzle platformer
Was 3 years a long time for an indie platformer in the early 2000s? Looking at some similar examples:
* Braid was 3 years
* Cave Story was 5 years
* World of Goo was 2 years
* Limbo was about 3 years (but with 8-16 people)
So Braid seems pretty average.
Yeah, you’re right. I must’ve misremembered.
I feel like a game is a bit different though because you control the input. You can't profile input you've never seen.