back

by Alifatisk·3y ago·view on hn ↗
I don’t exactly know how but I’ve always connected GC-languages with slow performance, but today, I realized how wrong I was.
5 comments
Performance and GC is a tricky topic, partly because there's not so many GCd languages explicitly designed for performance above usability (maybe D would count? maybe Go?). GC is normally chosen for usability reasons, and then the language has other usability features that reduce performance and it gets difficult to disentangle them. Immutability is a common problem. GC makes allocating lots of objects easy, so people make immutable types (e.g. Java's String type) and that forces you to allocate lots of objects, which causes lots of cache misses as the young gen pointer constantly moves forwards, and that slows everything down whereas a C++ dev might shorten a string by just inserting a NULL byte into the middle of it. Functional programming patterns are a common culprit because of their emphasis on immutability. You bleed performance in ways that don't show up on profiles because they're smeared out all over the program.

Another complication is that people talk about the performance of languages, when often it's about the performance of an implementation. The most stunning example of this is TruffleRuby in which the GraalVM EE Ruby runtime often runs 50x faster or more than standard Ruby. Language design matters a lot, but how smart your runtime is matters a lot too.

A final problem is that many people associate GC with scripting languages like Python, JavaScript, Ruby, PHP etc and they often have poor or non-existent support for multi-threading. So then it's hard to get good performance on modern hardware of course and that gets generalized to all GC languages.

Well, there's still truth to it in other cases, I think. One terrible thing GCs can do is make your performance unpredictable. In some performance-sensitive situations (eg: video games), your worst-case perf is more important than your average case. Adding a GC can mess with that worst-case behavior, and in unpredictable ways.

That being said, modern GCs are much better (less "stop the world" stuff), and more configurable. But it's still a real concern.

GCs also have very different memory access behavior, and so can be worse on a system with swap (or use more power on a system without one) even if theoretically they're more efficient.
To beat a GC, you need really tight memory management. It can mean things like buffer reuse, bulk allocations, custom allocators, etc... But if you do, depending on your workload, you can get big performance improvement. Video games commonly use these techniques.

The downside, obviously, that it is a lot of effort. It is also unsafe.

And in the end, that's what make GC languages "slow". It is not really that the GC is slow, it is that programmers using GC languages tend to prefer productivity and safety over performance, they will usually not optimize their code as much, and they will generally avoid unsafe features, it they are available at all. Programmers using non-GC languages will usually focus more on performance, which is usually the reason why they have chosen a non-GC language in the first place.

As I always say, C usually tops the chart when it come to language performance. But it is not because of some kind of C magic, it is that it forces you to micromanage your code, and especially your memory. If you use some kind of framework that gives you GC-like features in C, you will be on the same level as other GC languages, sometimes slower, because that GC-like thing may not be as optimized as a true native GC.

As far as using GC-like features from C goes, one benefit is that it isn't all or nothing. If you use Java, you basically need to have everything be GC'd. If you use C (or something similar like C++ or D), you can pick and choose from the most appropriate memory management techniques for different parts of your code.
> As I always say, C usually tops the chart when it come to language performance.

This has not been true for a long time. Modern C++ consistently outperforms C in practice, mostly due to its extensive metaprogramming facilities.

C++ has more potential (indeed thanks to metaprogramming), but the average idiomatic C program is often faster. C++ tends to do a lot of implicit allocation, or initialize values even if not necessary. Also, smart pointers are not free (at least shared_ptr and weak_ptr are not).

If you have a good understanding of how C++ works, eschew convenient but costly C++ features, and care to optimize. You may get better performance than C. I guess that's part of the reason why most game engines are made in C++.

Important to note though that you will only get fast if you turn on compiler optimization, which actually is a problem as debug builds may end up unusably slow.

GC just describes a set of algorithms that track and manage resources at runtime. GC-languages would be languages that use a GC for all or nearly-all memory management.

But non-GC languages can absolutely use GC algorithms. It's actually pretty common - Chromium uses GC in C++, for example. This lets you selectively choose where your GC applies and tune it for not just the application but for that specific bit of the application.

There are lots of interesting algorithms for this that solve exactly the problem of "I have a concurrent data structure and I had to swap out a pointer" without a refcount - for example, epoch based collection.

The "slow" here is very relative.

Yes, you can't push Discord -levels of messages with a GC language (like their famous "we moved from Go to Rust" post told us all).

But you're not going to be at those levels any time soon, GC pauses are just fine in a good 99.99% of cases.

This kind of thinking is a type of premature optimization, you skip whole categories of technology (garbage collection) because you "feel" it's slow based on what you read about an edge case. =)

Except whatsapp at least used to run on Erlang, which definitely was pushing that kind of data.

GC languages can have much better performance than non-GC languages (for many reasons, several of them mentioned here), and the main disadvantage being unpredictability. However, the unpredictability is not going to be problem for a chat application.

> Yes, you can't push Discord -levels of messages with a GC language (like their famous "we moved from Go to Rust" post told us all).

You can to a (pretty big) degree, as Discord have publicly stated in engineering blog posts:

- https://discord.com/blog/how-discord-scaled-elixir-to-5-000-...

- https://discord.com/blog/using-rust-to-scale-elixir-for-11-m...

IIRC they fixed a worst case latency of like 300ms in their LRU cache. These were periodic spikes. I doubt users really noticed a change from this. Maybe, it saved them some money...
99.9% of those posts from startup engineers saying "we migrated from X to Y" is because they wanted to play with technology Y and rationalised their decision.