Smart pointers instead of GC?

Kiith-Sa kiithsacmp at gmail.com
Mon Dec 31 06:05:00 PST 2012


I think you're overthinking this way too much.

I'm writing a game engine, where latency is much more important
than in your case. Even 10ms would translate to visible jitter.
GC's not an issue, and disabling it permanently is very
counterproductive _unless_ you've exhausted all other options
(which you're unlikely to, as you can do everything you can do in
C++).  All you have to do is care about how you allocate and, if
GC seems to be an issue, profile to see _where_ the GC is being
called most and optimize those allocations.

Basic rules:

For classes with one or few instances (singletons, etc.), GC is
not an issue. For classes with hundreds-thousands of instances,
it might be an issue. Profile. For classes with more instances,
it probably is an issue. Profile, reuse instances, use structs,
manually allocate.  Arrays: To avoid reallocation on append, use
array.assumeSafeAppend() before the append (Assumes that the
array is not a slice of a bigger array that would get its data
overwritten).

If appending a lot, use std.array.Appender. If you need manually
allocated storage, use std.container.Array, or create a wrapper
around malloc/free. I'm using a templated wrapper that allows me
to record how much memory/instances was allocated with which
types.  Destructors: Structs are RAII like in C. And you can
easily create a malloc wrapper that will act exactly like
new/delete in C++.

Classes: call destroy(instance). Calls the dtor, but doesn't
deallocate the class (GC will do that later). Not much different
from deleting a class allocated through new in C++ (which is what
you do most of the time in C++).  If you absolutely need to free
the memory, create malloc/free wrappers for classes.  In my case,
classes are used for polymorphic stuff, which are usually single
or few-instance objects. Structs are used for most of the
many-instance objects (e.g. components of game entities), and are
usually stored in manually allocated arrays.

GC is _very_ useful for stuff like closures, which can greatly
simplify some code. Also, while I maintain that disabling GC
completely is a bad idea, it might be useful to disable it in a
small area of code _after_ profiling shows that GC is being
called too much there.

In my YAML parser library, I've found after profiling that 18% of
time was spent in GC. Most of that was in a small piece of code
that repeatedly allocated memory. Disabling GC before this code
and reenabling it after (scope(exit) to avoid leaking disabled
GC) resulted in GC taking ~2% of time, because many unnecessary
collects were consolidated into one after the GC was reenabled.
Memory usage didn't take a hit, because this was actually a
fairly small piece of code, not doing too many allocations per
call, just called very often.


More information about the Digitalmars-d mailing list