Hacker News new | ask | show | jobs
by tkrskxyz 1269 days ago
>Avoid bugs related to mutable state.

And generate a lot of overhead both in execution time and memory. Which is unacceptable in games where every ms counts.

>Games tend to define update() functions which mutate some state each tick. We can avoid mutation by taking game state as an argument and returning new state. Now our game is much easier to test too! We simply call update with some game state and assert that the result is what we expect. Imagine how hard it would be test the imperative update function!

As a game dev I kinda cringe on the idea of copying WHOLE world state just to update it (and if I understand correctly we copy the state EVERY TIME we want to update i.e: from every actor instance?).

4 comments

Immutable data structures prevent you from having to copy the entire state. In fact the first "copy" is essentially a no-op. Compare it to a "copy on write" filesystem, where the changes you make are the only thing that needs to be copied/written.

https://en.wikipedia.org/wiki/Persistent_data_structure

On the other hand it is kringe-worthy, when people use mutable state and then are unable to properly parallelize the workload onto multiple cores. I mean, if I understand correctly, we have many cores sitting around and only 1 is working? Sometimes also a few, before we hit the bottle neck of mutual exclusion.
On the other other hand, if you've got a game state, and you want multiple cores to do the update, and you're not using mutation, then when core 1 makes a change (from state 0 to state 1, say), then when core 2 makes a change, it has to start from state 1, not from state 0, so that core 1's change doesn't get lost. So then you have the problem of updating all the cores (and all the threads) with state 1 before any of them make any further changes.

The way I would do this imperatively with N cores is, I would have the game state be an array, and I would have each core update 1/Nth of the array. That might thrash the cache, but it wouldn't need any locking at all.

Functionally, it could probably be done better to have each core produce a new sub-state, and then have one thread combine the N sub-states into the new state, and update all the threads with the new combined state.

Not to disagree, but to be fair modern fp runtimes use data structures that wouldn't have to copy so many bytes as the whole state.

A nice showcase of this idea is in Juan Pedro Bolivar Puente's CppCon '17 talk Postmodern Immutable Data Structures: https://youtu.be/sPhpelUfu8Q

Maybe don’t comment on things you don’t know much about?

Lazy data structures can be very efficient (slower than mutable one in single-threaded execution, but much faster for concurrent usage) and they definitely don’t copy everything every time.

Let's remember that we are having this conversation in the context of this article.

I know something about lazy data structures from my IOI days and I'm pretty sure I know about game development (altough admittedly forgot about this stuff while reading this post as it is not mentioned in the article itself, others comments provided nice resources).

While they are extremely efficient you still have an overhead. It is obviously minimal but it can matter with -+10000 updates (game logic, actors only, with some overhead by itself) per frame - not per second. O(log n) is not dissmisible (in some hardcore cases I would say that even O(log log n) can have slight impact and If you can have steady 60fps rather than 59fps you want that).

Game development also doesn't really align with functional programming (as described in the article). I mean the main thing to care about is speed since it allows us to do more fancy stuff. Then again, I'm not an engine developer so who knows, maybe it's actually better and some big engines do use it at core (If anyone knows I'm interested). But in most cases it's a lot harder to do (since games do SO much different stuff that It's really hard to seperate parallelizable from sequential tasks anyway and it simply doesn't make sense in most productions).