Live data from Hacker News

Why I Write Games in C (yes, C)

jonathanwhiting.com

471–480 of 556 posts

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

#471
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…

I know this subject quite well and I will later publish a detailed article. The real run-time cost of memory management done well in a modern game engine written without OOP features is extremely low. We usually use a few very simple specialized memory allocators, you'd probably be surprised by how simple memory management can be. The trick is to not use the same allocator when the lifetime is different. Some resourc…

> a modern game engine written without OOP features

Is there a particular codebase you are thinking of here?

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

#472
post #81

Earlier quoted context omitted.

The guy should try Rust and then explain why he prefers either, that would be interesting.

I like rust, but for me, it is significantly more difficult to read and write. Same can be said about C++ with extreme STL code.. I prefer to read and write simple C like syntax. I would love it if I can get Rust concept of borrowing in C, or even simple operator overloading..

Ironic, the most complicated thing for me to understand when I started writing rust was borrowing and lifetimes. The rest of the common features of Rust isn't too complicated to understand.

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

#473
post #323

Earlier quoted context omitted.

In which the garbage collector serves no advantage; only as a trap you have to remind yourself to step over.

It serves the huge purpose of doing 100% of the tracking work. Whats easier? Manually tracking all of your allocations and frees as well as worrying about double frees and pointer ownership or calling GC.Collect() during a load screen?

What's easier? Knowing that you can use all your memory again after you've explicitly freed the resources you used, or knowing that you can use all your memory again after a call to GC.Collect() does god-knows-what?

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

#474
post #426

Earlier quoted context omitted.

Strict typing afaik isn't really a formally defined, imo. As you know C is statically typed and generally said to be "weakly typed" as well but you can make a strong argument that just about any language is "strong" or "weak" -ly typed based on a dozen or so characteristics. I think it makes more sense to treat it as a spectrum where some languages are "stronger" than others. Which is different than in the case of "s…

Oh come on, I was taught the difference between strong and weak typing with C (weak) and Pascal (strong) as the examples 30 years ago. You can cast a C entity to something else and NO ONE ELSE KNOWS. Then THINGS happen. BAD things.

While Pascal is indeed strong typing and C is weak typing, casting is allowed on both at compile time and introducing bugs this way is not going to be stopped by compiler. Here is what a compile will behave in C vs Pascal:

C code:

int i = 1; char s = "1";

{

if (i == s) //{

//bla bla

}

}

Pascal code:

var

i : integer = 1;

s : string = '1';

begin

if i = s then begin ////bla bla

end;

end.

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

#475

Earlier quoted context omitted.

The semantics matter. A lot of game engines use a mark-and-release per-frame allocation buffer. It is temporary throwaway data for that frame's computation. It does not get tracked or freed piecemeal - it gets blown away. Garbage collection emulates the intent of this method with generational collection strategies, but it has to use a heuristic to do so. And you can optimize your code to behave very similarly within…

> A lot of game engines use a mark-and-release per-frame allocation buffer. I've heard of this concept but a search for "mark-and-release per-frame allocation buffer" returned this thread. Is there something else I could search?

Linear or stack allocator are other common terms. Just a memory arena where an allocation is just a pointer bump and you free the whole buffer at once by returning the pointer to the start of the arena.

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

#476
post #170

Earlier quoted context omitted.

Once I wrote a very small vector library in JS for this very reason: almost all JS vector libraries out there tend to dynamically create new vectors for every vector binary operation, this makes JS GC go nuts. It's also prohibitively expensive to dynamically instantiate typedarray based vectors on the fly, even though they are generally faster to operate on... most people focus on fixing the latter in order to be abl…

You wouldn't have this still up somewhere like GH would you? I'm currently writing a toy ECS implementation and have somewhat similar needs, and I've been trying to build up a reference library of sorts covering novel ways of dealing with these kind of JS issues

Three.js implements their math this way.

https://github.com/mrdoob/three.js/blob/master/src/math/Vect...

You can see most operations act on the Vector and there are some shared temporary variables that have been preallocated. If you look through some of the other parts you can see closures used to capture pre-allocated temporaries per function as well.

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

#477
post #20

Earlier quoted context omitted.

> Write C code. Compile with C++ compiler. What C only compiler were you thinking of? Most of the ones I can think of are toy compilers. If you're making a game, you're going to be using clang/MSVC/gcc. For catching bugs early, at least as important as the compiler are * Enabling additional compiler warnings and -Werror * Checking for memory leaks with valgrind * checking for Undefined Behavior with UBSan * Using mix…

All of those compiler families have separate C and C++ compilers. They’re saying to use g++ to compile your .c’s instead of gcc.

Assuming OP is correct, I learned something new today. I knew running g++ on .c would produce different results based on differences in the language like sizeof('x') and differences in extern. A quick google didn't turn up any differences between warnings between g++ and gcc. I'll have to look into this.

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

#478
post #439

Earlier quoted context omitted.

Exception handling doesn't work very well. It requires exception frames on the stack, and those go missing sometimes, leading to leaked exceptions and unexpected crashes. For example, if you pass a C++ callback function into a C callback, say in libcurl, the C library doesn't have exception handler frames in the stack, so exceptions are lost. If your C++ code throws from within that C callback, any catch above the C…

Don't pass functions to libcurl that throw. Otherwise, exceptions make code simpler, cleaner, and more reliable. They will never "go missing" unless you do something to make them go missing. Code that does those things is bad code. Don't write bad code. Do use exceptions.

Well, of course, that's how you fix such a bug, however, when using third party libraries and other people's code, you don't necessarily know that this is happening until you have missing exceptions.

In my opinion, exceptions also complicate code by taking error handling out of the scope of the code you are writing, since you can't know whether anything you call throws, so you may not catch, and some higher level code will catch an exception, where it's lost context on how to handle it. If exceptions work well in a language; Python, Java, by all means use them, but in C++, they've caused too many problems for me to continue doing so. Even Google bans them in their C++ style guide.

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

#479

Earlier quoted context omitted.

+1. We have a large Rust code base, and we forbid Vec and the other collections. Instead, we have different types of global arenas, bump allocators, etc. that you can use. These all pre-allocate memory once at start up, and... that's it. When you have well defined allocation patterns, allocating a new "object" is just a "last += 1;` and once you are done you deallocate thousands of objects by just doing `last -= size…

Do you use any public available crate for those allocators? Would love to take a look. I'm currently trying to write a library for no-std which requires something like that. I currently have written a pool of bump allocators. For each transaction you grab an allocator from the pool, allocate as many objects from it as necessary, and then everything gets freed back to the pool. However it's a bit hacky right now, so I…

> Do you use any public available crate for those allocators?

Not really, our bump allocator is ~50 LOC, it just allocates a `Box` with a fixed size on initialization, and stores the index of the currently used memory, and that's it.

We then have a `BumpVec` type that uses this allocator (`ptr`, `len`, `cap`). This type has a fixed-capacity, it cannot be moved or cloned, etc. so it ends up being much simpler than `Vec`.

Post reply on HN