Why many programmers don't like GC?

H. S. Teoh hsteoh at quickfur.ath.cx
Fri Jan 15 07:35:00 UTC 2021


On Thu, Jan 14, 2021 at 12:36:12PM +0000, claptrap via Digitalmars-d-learn wrote:
[...]
> I think you also have to consider that the GC you get with D is not
> state of the art, and if the opinions expressed on the newsgroup are
> accurate, it's not likely to get any better. So while you can find
> examples of high performance applications, AAA games, or whatever that
> use GC, I doubt any of them would be feasible with Ds GC. And given
> the design choices D has made as a language, a high performance GC is
> not really possible.

To be fair, the GC *has* improved over the years.  Just not as quickly
as people would like, but it *has* improved.


> So the GC is actually a poor fit for D as a language. It's like a
> convertible car with a roof that is only safe up to 50 mph, go over
> that and its likely to be torn off. So if you want to drive fast you
> have to put the roof down.

How much D code have you actually written and optimized?  That analogy
is inaccurate.  IME, performance issues caused by the GC are generally
localized, and easy to fix by replacing that small part of the code with
a bit of manual memory management (you *can* rewrite a function not to
use the GC; this isn't the Java straitjacket, y'know!), or standard GC
optimization techniques like reducing GC load in hot loops.  There's
also GC.stop and GC.collect for those times when you want more control
over exactly when collection pauses happen.

I wrote a compute-intensive program once, and after some profiling
revealed the GC being a bottleneck, I:

(1) Refactored one function called from an inner loop to reuse a buffer
instead of allocating a new one each time, thus eliminating a large
amoun of garbage from small allocations;

(2) Used GC.stop and scheduled my own GC.collect with slightly reduced
frequency.

The result was about 40-50% reduction in runtime, which is close to
about a 2x speedup.

Now, you'll argue that had I written this code without a GC in the first
place I wouldn't have needed to do all this.  However:

(a) Because I *had* the GC, I could write this code in about 1/5 of the
time it would've taken me to write it in C++;

(b) The optimization involved only changing a couple of lines of code in
2-3 functions -- a couple of days' work at most -- as opposed to blindly
optimizing *every* single danged line of code, 95% of which wouldn't
even have had any noticeable effect because they *are not the
bottleneck*;

(c) The parts of the code that aren't in the hot path can still freely
take advantage of the GC require minimal effort to write, and be free of
the time-consuming bugs that often creep into code that manually manages
memory.

As I said, it's an ROI question. I *could* have spent 5x the amount of
time and effort to write the perfect, GC-less, macho-hacker-style code,
and get maybe about a 1-2% performance improvement. But why would I?  It
takes 5x less effort to write GC code, and requires only a couple more
days of effort to fix GC-related performance issues, vs. 5x the
development effort to write the entire program GC-less, and who knows
how much longer after that to debug obscure pointer bugs.  Life is too
short to be squandered chasing down the 1000th double-free and the
20,000th dangling pointer in my life.

A lot of naysayers keep repeating GC performance issues as if it's a
black-and-white, all-or-nothing question.  It's not.  You *can* write
high-performance programs even with D's supposedly lousy GC -- just
profile the darned thing, and refactor the hotspots to reduce GC load or
avoid the GC. *In those parts of the code that actually matter*.  You
don't have to do this for the *entire* lousy program.  The non-hot parts
of the code can still GC away like there's no tomorrow, and your
customers would hardly notice a difference.  This isn't Java where you
have no choice but to use the GC everywhere.

Another example: one day I had some spare time, and wrote fastcsv
(http://github.com/quickfur/fastcsv).  It's an order of magnitude faster
than std.csv, *and it uses the evil GC*.  I just applied the same
technique: write it with GC, then profile to find the bottlenecks. The
first round of profiling showed that there tend to be a lot of small
allocations, which create lots of garbage, which means slow collection
cycles.  The solution? Use a linear buffer instead of individual
allocations for field/row data, and use slices where possible instead of
copying the data.  By reducing GC load and minimizing copying, I got
huge boosts in performance -- without throwing out the GC with the
bathwater.  (And note: it's *because* I can rely on the GC that I can
use slices so freely; if I had to use RC or manage this stuff manually,
it'd take 5x longer to write and would involve copying data all over the
place, which means it'd probably lose out in overall performance.)


But then again, it's futile to argue with people who have already made
up their minds about the GC, so meh. Let the bystanders judge for
themselves. I'll shut up now. *shrug*


T

-- 
Study gravitation, it's a field with a lot of potential.


More information about the Digitalmars-d-learn mailing list