[1] especially when comparing to Java when it comes to GC customization.
[1] especially when comparing to Java when it comes to GC customization.
I was most impressed that he managed to get very close to perfect scalability by twiddling the one knob on offer.
Java GC customisation is an art. This analysis was entirely mechanical. If the author wasn't just lucky i.e. that this is sufficient for a large number of workload profiles, then that's very good news.
That said, there are exceptions. I don't think GC is something simple enough to be an exception, but those exist.
I expect Go's current behaviour is right for memory-intensive programs, where "limit the wasted memory as a ratio of the actual working set" is a reasonable high-level goal. For a CPU intensive program you might have preferred a "max x% CPU overhead" knob, but just a minimum memory size would work, as this is mostly an artifact of the very small memory usage of benchmark program.
Java's G1 has "max pause delay" as their preferred knob, but Go has stuck that knob on low since around version 1.6, which is a good choice for what is essentially a language for network services.
I am surprised that the difference is so great - and that the GC seems to think it's a good idea to keep collecting 4 MB of data at a time - but I guess the Go team have been tuning heavily for interactive performance i.e. small pauses.
I guess in a situation like this you'd prefer something more like 'server' GC - maybe wait until you've allocated 1 GB then run through and collect everything you can.
In games, I wonder if you'd like to turn the GC off during frame rendering then collect while presenting the frame/waiting for VSync.
For soft real-time audio - synthesizing a few samples at a time - what would you do? OpenAL wants you to queue up thousands or tens of thousands of samples, but that sucks if you need to react very quickly.
If anyone has any ideas, please say. I'd love to do soft real-time audio in a thread even in e.g. C#. (Edit: Maybe JIT some code that doesn't allocate? Or even have a separate interpreter written in C that has a constant-ish upper performance bound & no malloc.)
As another comment in this thread explained, GCs have CPU and Memory tradeoffs and you can easily make a GC that would work excellent in the OP benchmark but would suffer severely under other workloads.
There are lots of good criticisms of Go to be had, but it's runtime is pretty remarkable, in my opinion.
Or maybe not - maybe that one knob really can solve all the problems. But I really was surprised that anyone would ever need to actively configure a GC for such a simple use case, so I've learnt something at least.
Actually, GC == automatic memory management, just not automatic performant memory management
Well, it seems we don't have a simple GC that performs well in all cases, and really why would we expect to? So choosing simplicity means leaving performance on the table, at least for now, and while there are languages that do that (Python) it doesn't seem like a good fit for Go.
We don't appear to be much better off than we were with C. I guess the server melting is probably preferable to an exploitable smashed stack, but prevention of both issues appears to be to hire geniuses who don't make mistakes while doing enormously mistake-prone work.
"Everyone gets correctness, you need geniuses to get maximum performance" is a huge improvement over the reverse.