Live data from Hacker News

Why I Write Games in C (yes, C)

jonathanwhiting.com

191–200 of 556 posts

Re: Why I Write Games in C (yes, C)

#191
post #31

Earlier quoted context omitted.

Implying that typing does not prevent bugs is the strangest programming meme I’ve ever heard, and its proponents push it so hard that I wonder if there’s somewhere I can sign up to be paid for it. We rarely are able to directly compare the cost/benefit of strict typings vs loose typings, but with JS vs TS you get a pretty direct comparison, and it is absolutely unsurprising that TS is eating the JS world; it does pre…

> TS is eating the JS world Definitely another reason to stick to C. In C you don't have to change to another language or another framework, or yet another design principle, or whatever hype that is being followed by a horde of idiots that think they're incredibly smart. C is still C. I love that so much. No endless discussions about type safety. And yes, with C I can shoot myself in the foot, which is great because…

The problem with C isn’t you shooting yourself in the foot. The problem is that off-by-one errors and buffer overruns that are trivially easy to produce in C turn into security bugs that affect your users.

If you don’t have any users, fine, but no one writing a nontrivial program for use by other people should be doing it in straight C at this point if it’s at all possible to avoid.

Re: Why I Write Games in C (yes, C)

#192
post #167
post #151

Earlier quoted context omitted.

"Clearing an 18GB heap" that's full of 100MB objects that are 99% dead is different than clearing an 18GB heap of 1KB objects that are 30% (not co-allocated, but randomly distributed across the whole heap).

this is exactly the kind of dismissive hand-waving that is frustrating. The 18gb heap example is from data collected on production servers at Twitter. Go servers routinely juggle tens of thousands of connections, tens of thousands of simultaneous user sessions, or hundreds of thousands of concurrently running call stacks. We're essentially never talking about 100mb objects since the vast majority of what Go applicati…

I'm not a game developer, just a programming language enthusiast with probably above average understanding of how difficult the problem this is.

Can you point out in the post where they expand on my point? The only this I see is this:

> Again we kept knocking off these O(heap size) stop the world processes. We are talking about an 18Gbyte heap here.

which is exactly my point - even if you remove all O(heap size) locks, depending on the exact algorithm it might still be O(number of objects) or O(number of live objects) - e.g. arena allocators are O(1) (instant), generational copying collectors are O(live objects), while mark-and-sweep GCs (including Go's if I understand correctly after skimming over your link) are O(dead objects) (the sweeping part). Go's GC seems to push most of that out of Stop-The-World pause, instead it offloads it to mutator threads instead... Also, "server with short-lived requests" is AFAIK a fairly good usecase for a GC - most objects are very short-lived, so it would be mostly garbage with simple live object graph...

Still, a commendable effort. Could probably be applied to games as well, though likely different specific optimisations would be required for their particular usecase. I think communication would be better if you expanded on this (or at least included the link) in your original post.

Re: Why I Write Games in C (yes, C)

#193

Earlier quoted context omitted.

Interestingly, it's fully possible to disable the automatic garbage collection in Go to achieve this. Disable the garbage collector: debug.SetGCPercent(-1) Trigger garbage collection: runtime.GC() It is also possible to allocate a large block of memory and then manage it yourself.

Due the low throughput of Go's GC (which trades a lot of it in favor of short pause duration), you risk running out if memory if you have a lot of allocations and you don't run your GC enough times.

For a computer game, if you start out by allocating a large block of memory, then manage it yourself, I don't see how this would be a problem.

Re: Why I Write Games in C (yes, C)

#194
post #46

> The stop-the-world garbage collection is a big pain for games, stopping the world is something you can't really afford to do. I love this opinion from games programmers because they never qualify it and talk about what their latency budgets are and what they do in lieu of a garbage collector. They just hand wave and say "GC can't work". The reality is you still have to free resources, so it's not like the garbage c…

> The reality is you still have to free resources

Err not so fast there. It's quite common in C to allocate an array of objects (not pointers) and reuse them as they expire. Memset is enough to reinitialize them.

And the the main point, lot's of game dev is "C wrapped in C++", game devs tend to rewrite everything for performance and predictability, relying on STL is usually a nope.

Re: Why I Write Games in C (yes, C)

#195
post #46

> The stop-the-world garbage collection is a big pain for games, stopping the world is something you can't really afford to do. I love this opinion from games programmers because they never qualify it and talk about what their latency budgets are and what they do in lieu of a garbage collector. They just hand wave and say "GC can't work". The reality is you still have to free resources, so it's not like the garbage c…

You can just use a GC that doesn't collect until you tell it to and just call it during load screens and preallocate all your object pools. Its not that bad.

The trick is using libraries and techniques that only stack allocate or use your pools. These are techniques you'd almost certainly use without a GCed language but somehow people consider using C before using these techniques is a higher level language.

Re: Why I Write Games in C (yes, C)

#196
post #46

> The stop-the-world garbage collection is a big pain for games, stopping the world is something you can't really afford to do. I love this opinion from games programmers because they never qualify it and talk about what their latency budgets are and what they do in lieu of a garbage collector. They just hand wave and say "GC can't work". The reality is you still have to free resources, so it's not like the garbage c…

Unity uses/used the standard boehm garbage collector [1] for over a decade, and it has been notorious for causing GC lag in games produced from that engine, noticeable in occasional sudden spikes of dropped framerate while the GC does a sweep at a layer of abstraction higher than Unity game developers can control directly.

People went to extreme measures to avoid allocating memory in their games: manually pooling every in-game object & particle, not using string comparisons in C#, etc https://danielilett.com/2019-08-05-unity-tips-1-garbage-coll...

Unity itself finally has a new system they're previewing to average out the GC spikes over time, so a game, say, never drops below 60fps: https://blogs.unity3d.com/2018/11/26/feature-preview-increme...

As well, there is a new way of writing C# code for Unity called ECS that will avoid producing GC sweeps https://docs.unity3d.com/Packages/com.unity.entities@0.1/man...

[1[ https://github.com/ivmai/bdwgc

Re: Why I Write Games in C (yes, C)

#197
One of my dream side-projects is to create something inspired by Ken Thompson's C (the plan9 compiler), with some ideas borrowed for Go, where this "almost C" language could be written in a valid C just like you can do it with C++.

things like:

(Composing structs)

    struct Parent {int number}
    struct Child {Parent; int another_number} // anonymous struct convention

    Child c;
    c.number = 10;
    c.another_number = 11;
(Object notation, namespacing)

    int DoThis(Child* this, int input) {}
    Child* c ...
    c->DoThis(10)
(Someway to define public/private in structs and methods, like Go do with convention of Lowercase/Uppercase components/function names)

and Finally

(Someway to define a interface and use vtables when needed just like Go´s Interface but in a more C compatible way like.. )

    struct MyInterface {
     int (*Read)(int a, int b);
     int (*Write)(int a, int b);
    }
or

    struct MyInterface {
     int Read(int);
     int Write(char);
    }

    struct X {}

    int Read(X* this, int n) {}
    int Write(X* this, char c) {}

    X x = X{};

    x.Read(10);
    x.Write('b');  
    MyInterface* c = &x;

    (...)
(templates) - C++ way is fine

Thats it. With all this you would have "a better C", with compability with C codebase and still a language much simpler than C++, yet powerful.

Re: Why I Write Games in C (yes, C)

#198
post #98

Earlier quoted context omitted.

as an example point, the Go garbage collector clears heaps of 18gb in sub-millisecond latencies. If I'm understanding the problem at hand (maybe I'm not!), given an engine running at a target framerate of 144 frames per second, you're working with a latency budget of about 7ms per frame. Do you always use all 7ms, or do you sometimes sleep or spin until the next frame to await user input? We can also look at it from…

There is no general answer to this question. Frame latency, timing and synchronization is a difficult subject. Some games are double or triple buffered. Rendering is not always running at the same frequency as the game update. The game update is sometimes fixed, but not always. I've had very awful experience with GC in the past, on Android, the game code was full C/C++ with a bit of Java to talk to the system APIs, I…

You didn't have a bad experience with GC in the past, you had a bad experience with a single GC implementation, one which was almost certainly optimized for throughput and not latency and in a language that pushes you toward GC pressure by default. :)

Re: Why I Write Games in C (yes, C)

#200

Isn’t Rust an alternative? I don’t know Rust well enough, but wouldn’t that fit the bill?

It is if you're writing a game from scratch, without need to deeply integrate with something like Unreal Engine.

I used to write games in plain C, and nowadays I'd definitely use Rust for them.

There is a small Rust games community and a couple of engines/frameworks: https://lib.rs/game-engines

One thing with Rust is that it pretty much requires use of entity-component-system. It's the best practice for real-world games anyway, but people who write their first game are surprised they can't just "wing it" with some ad-hoc OOP.

Post reply on HN