4 ms·
As an ex-3D engine dev, I respectfully disagree. But if you're happy to give some examples of why you can't constrain your data-types then I'm happy to admit I
by louthy 4y ago
As an ex-3D engine dev, I respectfully disagree. But if you're happy to give some examples of why you can't constrain your data-types then I'm happy to admit I'm wrong.
I realise sometimes there's aspects of a game that needs to write to the metal, and compromises need to be made, but that isn't necessary for an entire game. I know people are using my language-ext library with Unity projects too, so I assume there are others in games that don't agree (although I don't know exactly what kind of titles they're working on).
[1] https://github.com/louthy/language-ext https://github.com/louthy/language-ext
- Jensson 4y ago3d engine is very structured, it isn't the creative part of game development. Lets say you want to test your bullets having HP and can be shot down. So you add that in an hour, now bullets have HP, you test it, it doesn't work, so you remove it. Things like that needs to be seamless, because you need to iterate on ideas and test if things are fun. Big upfront design as you suggest where you very carefully constrain everything doesn't do well with that. It works well for 3d engines for example sure, but 3d engines are much more constrained than games.
- louthy 4y ago> 3d engine is very structured, it isn't the creative part of game development. I also did lots of the creative bit ;). I just assumed this was going to go down a performance route question, sorry! > Big upfront design as you suggest No, I am not suggesting that at all - I'm saying the opposite of that. You're the third person to think that's what I am saying now, so I assume I didn't explain myself very well. Rather than repeat myself again, my other replies cover this [1] [2]: [1] https://news.ycombinator.com/item?id=34847413 https://news.ycombinator.com/item?id=34847413 [2] https://news.ycombinator.com/item?id=34847326 https://news.ycombinator.com/item?id=34847326
- Jensson 4y agoWell, you say that works, but basically nobody who programs creatively writes code that way. People who like functional programming mostly writes compilers or similar processing throughput systems, you see it a ton there, but you barely see it on the other side of the programming landscape such as game programming. So, you say it works for you, but basically every example of the things I talked about are done in a very different way than you describe, so unless you have programmed some very impressive games I would say that the data strongly supports another style of programming than you suggest here. For compilers or parsers or stuff like medical systems? Sure, go ahead, it seems to work for those domains, but it doesn't seem to be a panacea. If games were so much easier to write that way we would see tons of them, especially among the better games, but we don't.
- louthy 4y agoThe biggest mistake you make is thinking you do something different to me or any other software engineer. You don't. You do the same thing and the same rules apply. You seem to think you're the only person doing anything creative. Well here's a newsflash: it's the same process in games as it is in all other forms of software development. The end result might look different, but the process is the same. The best devs are the creative ones, but creativity isn't limited to what appears on the screen, it comes from architecting complex solutions to difficult problems, it comes from elegance in code, and it comes from producing something that works for the end-user - whether that's a gamer, a doctor, or someone browsing on their phone. Games is one of the easiest sectors to be in, the complexity level is low compared to other sectors, especially now all the difficult bits are done for you (off the shelf engines). I assume you're not working on titles that have 15 million lines of code in, like I do every day. That is possibly why there hasn't been a big push for FP in games yet, because they haven't yet hit the complexity limit of OO like others have. Your argument is "Well we've always done it like this, why change?". Probably for the same reason people don't write games in assembly languages any more: at some point the current tools aren't good enough. Games might not have hit that ceiling yet, but you'll almost certainly be affected by the artefacts of getting close to it. I see you're even questioning my history in games. Below [1][2][3][4][5][6][7][8] are some of the titles I worked on (or my code is in). But to be clear, we didn't use these techniques back then either, because it was too soon to be doing it (or we weren't enlightened then). You should consider being a little more open minded about how you could, possibly, write software more effectively. Especially when you have someone who's been on both sides of the fence advocating it. Just writing it off because you think people who do FP just write "compilers or similar processing throughput systems" is shooting yourself in the foot more than anything. [1] Battalion Wars: https://en.wikipedia.org/wiki/Battalion_Wars https://en.wikipedia.org/wiki/Battalion_Wars [2] Fire Blade: https://en.wikipedia.org/wiki/Fire_Blade_(video_game) https://en.wikipedia.org/wiki/Fire_Blade_(video_game) [3] Warmhammer 40,000 Fire Warrior: https://en.wikipedia.org/wiki/Warhammer_40%2C000%3A_Fire_Warrior https://en.wikipedia.org/wiki/Warhammer_40%2C000%3A_Fire_War... [4] Call of Duty - Finest Hour: https://en.wikipedia.org/wiki/Call_of_Duty:_Finest_Hour https://en.wikipedia.org/wiki/Call_of_Duty:_Finest_Hour [5] The Regiment: https://en.wikipedia.org/wiki/The_Regiment_(video_game) https://en.wikipedia.org/wiki/The_Regiment_(video_game) [6] Reign of Fire: https://en.wikipedia.org/wiki/Reign_of_Fire_(video_game) https://en.wikipedia.org/wiki/Reign_of_Fire_(video_game) [7] Attack of the Saucerman: https://en.wikipedia.org/wiki/Attack_of_the_Saucerman https://en.wikipedia.org/wiki/Attack_of_the_Saucerman [8] Lunatik [canned]: https://www.unseen64.net/2020/11/16/lunatik-pure-entertainment-cancelled/ https://www.unseen64.net/2020/11/16/lunatik-pure-entertainme...
- naasking 4y ago> [1] https://github.com/louthy/language-ext https://github.com/louthy/language-ext Cool library. I've had a few of these patterns in my Sasa library for years, but you've taken it to the Haskell extreme! Probably further than most C# developers could stomach. ;-) You might be interested in checking out the hash array mapped trie from Sasa [1]. It cleverly exploits the CLR's reified generics to unbox the trie at various levels which ends up saving quite a bit of space and indirections, so it performs almost on par with the mutable dictionary. I had an earlier version that used an outer struct to ensure it's never null [2], similar to how your collections seem to work, but switched to classes to make it more idiomatic in C#. I recently started sketching out a Haskell-like generic "Deriving" source generator, contrasted with your domain-specific piecemeal approach, ie. [Record], [Reader], etc. Did you ever try that approach? [1] https://sourceforge.net/p/sasa/code/ci/default/tree/Sasa.Collections/Trie.cs https://sourceforge.net/p/sasa/code/ci/default/tree/Sasa.Col... [2] https://sourceforge.net/p/sasa/code/ci/57417faec5ed442224a0f3a028f1982d459284db/tree/Sasa.Collections/Trie.cs https://sourceforge.net/p/sasa/code/ci/57417faec5ed442224a0f...
- louthy 4y ago> so it performs almost on par with the mutable dictionary. I notice you have a comment that says "This trie is now the fastest immutable dictionary I'm aware of". Unfortunately I have to make you aware that my implementation is faster, sorry! ;) Here's the add-items benchmark: BenchmarkDotNet=v0.13.5, OS=Windows 11 (10.0.22621.1265/22H2/2022Update/SunValley2) AMD Ryzen Threadripper PRO 3995WX 64-Cores, 1 CPU, 128 logical and 64 physical cores .NET SDK=6.0.300 [Host] : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT AVX2 DefaultJob : .NET 6.0.5 (6.0.522.21309), X64 RyuJIT AVX2 | Method | N | Mean | |-------------------------- |------- |---------------:| | SysColImmutableDictionary | 100 | 32.315 μs | | SasaTrie | 100 | 8.007 μs | | SysColDictionary | 100 | 1.440 μs | | LangExtHashMap | 100 | 7.512 μs | |-------------------------- |------- |---------------:| | SysColImmutableDictionary | 1000 | 524.368 μs | | SasaTrie | 1000 | 140.537 μs | | SysColDictionary | 1000 | 17.395 μs | | LangExtHashMap | 1000 | 125.663 μs | |-------------------------- |------- |---------------:| | SysColImmutableDictionary | 10000 | 7,598.532 μs | | SasaTrie | 10000 | 2,055.220 μs | | SysColDictionary | 10000 | 301.683 μs | | LangExtHashMap | 10000 | 1,842.514 μs | |-------------------------- |------- |---------------:| | SysColImmutableDictionary | 100000 | 129,811.705 μs | | SasaTrie | 100000 | 46,752.702 μs | | SysColDictionary | 100000 | 6,103.999 μs | | LangExtHashMap | 100000 | 37,869.597 μs | * SysColImmutableDictionary = System.Collections.Immutable.ImmutableDictionary * SysColDictionary = System.Collections.Generic.Dictionary My other benchmarks are still running, so I won't share them all, but we actually trade blows, your ContainsKey appears to be faster, my collection iteration is twice as fast as yours. I suspect if I removed my struct wrapper it would be a bit quicker, but wouldn't be a real-world fair comparison. We're both much faster than Microsoft's ImmutableCollections though! I am certainly interested in what that technique is with the nested Node<Node<Node<Node<... and why it works. Do you have any more detail on that? You might also want to look into the data-structure I use: CHAMP [1]. I notice you use HAMT, CHAMP is a more efficient version of HAMT. It has better data-locality, which is much more efficient especially when working with value-types. With regards to the 'deriving' question: Yeah, I'm working on a more general solution now, as well as a way to do monad-transformers properly. I've also figured out a way to do infinite tail-recursion for my monadic types, which I'll be deploying in the next major update. [1] https://michael.steindorfer.name/publications/phd-thesis-efficient-immutable-collections.pdf https://michael.steindorfer.name/publications/phd-thesis-eff... * EDIT * Here's the full suite of benchmarks, but I've trimmed them down to just the 10,000 item tests and removed the error and deviations for clarity: +-----------------------+----------------+ | Add 10000 items (value type) | +-----------------------+----------------+ | SasaTrie | 2,055.220 μs | | LangExtHashMap | 1,842.514 μs | +-----------------------+----------------+ | Add 10000 items (reference type) | +-----------------------+----------------+ | SasaTrie | 2,988.663 μs | | LangExtHashMap | 2,905.552 μs | +-----------------------+----------------+ | ContainsKey (10000 value items map) | +-----------------------+----------------+ | SasaTrie | 173,215.0 ns | | LangExtHashMap | 235,577.2 ns | +-----------------------+----------------+ | ContainsKey (10000 reference map) | +-----------------------+----------------+ | SasaTrie | 518.789 μs | | LangExtHashMap | 549.952 μs | +-----------------------+----------------+ | Iterate (10000 value items map) | +-----------------------+----------------+ | SasaTrie | 457,819.0 ns | | LangExtHashMap | 227,632.7 ns | +-----------------------+----------------+ | Iterate (10000 references map) | +-----------------------+----------------+ | SasaTrie | 712,623.1 ns | | LangExtHashMap | 519,250.7 ns | +-----------------------+----------------+ | Random remove (10000 value items map) | +-----------------------+----------------+ | SasaTrie | 2,276,772.4 ns | | LangExtHashMap | 1,765,042.9 ns | +-----------------------+----------------+ | Random remove (10000 references map | +-----------------------+----------------+ | SasaTrie | 2,946.775 μs | | LangExtHashMap | 2,576.813 μs | +-----------------------+----------------+