6 ms·
> This is a rather strong claim not supported by real-world data on how Go’s GC behaves under load The only thing you linked is an old binary trees benchmark f
by lossolo 2y ago
> This is a rather strong claim not supported by real-world data on how Go’s GC behaves under load
The only thing you linked is an old binary trees benchmark from the benchmarks game, which is not equivalent to the workload in a typical networked service. For that kind of workload, I would not pick Go.
> It’s okay to praise things you like and criticize those you don’t, but it can be kept within the realm of actual pros and cons of a particular technology.
Result of basic allocation micro-benchmark for 64 byte slices, one million per op, 4 cores:
BenchmarkAllocation/procs-4-10 115 10469109 ns/op 64000517 B/op 1000007 allocs/op
One operation makes 1000007 allocations.
Each operation takes 10469109 nanoseconds (0.010469109 seconds).
Per second 1 / 0.010469109 ≈ 95.52 operations.
So, allocations per second = 95.52 * 1000007 ≈ 95.5 million
You can keep your ad-hominem comments to yourself.
- neonsunset 2y agobinary-trees benchmark is using Go 1.23.0 which is the latest version. Go submissions do not do anything differently compared to other GC-based languages for the most part. The fastest Java submission has interestingly much lower execution time than other Java submissions, and I plan to look into it, but that would be pretty much the only caveat. I think it is relevant given the topic of the discussion is GC implementations and their characteristics. I have constructed the following micro-benchmark to replicate your data: https://gist.github.com/neon-sunset/c6c35230e75c89a8f6592cacbbd636ee https://gist.github.com/neon-sunset/c6c35230e75c89a8f6592cac... It tests - Time to allocate 1M 32B/64B/128B arrays/slices and pass each to an empty outlined method in a loop (to ensure heap allocation) - Same as above but 1M allocations are split between NumCPU Goroutines/Tasks. The environment is macOS 15, M1 Pro (8C), Go 1.23.0 and .NET 8. The Go results are 16.2ms, 18.2ms and 104.7ms for single-threaded execution and 11.1ms, 22.6ms and 48.3ms for multi-threaded. The C# results are 4.7ms, 5.9ms and 8.2ms for single-threaded and 1.8ms, 2.6ms and 3.8ms for multi-threaded. When selecting Workstation/Desktop GC instead of Server (Server is default for networked container workloads via asp.net core), the numbers don't exhibit multi-threaded scaling and are 4.7ms, 5.3ms and 7ms for single-threaded and 5.9ms, 9.4ms and 16.8ms for multi-threaded. This GC mode is used by default for console and light GUI application templates. Complex applications override it to Server GC. It is likely that it will be eventually deprecated because its target scenarios are now better addressed by the new mode of Server GC called DATAS, which is enabled by default in the upcoming .NET 9. If you're interested, feel free to replicate the data on your hardware, add more languages, etc. My assumption that OpenJDK will perform even better since Java code is more allocation-heavy. Regarding highly-loaded networked services with predictable or hand-tuned data lifetimes, in C# you are expected to completely bypass the allocations with the use of built-in ArrayPool<T>, stack-allocated buffers or NativeMemory.Alloc/Free which directly call into malloc/free. This is made possible by wrapping such buffers into Span<T> or Memory<T>. Things like Kestrel (web server) or Garnet (Redis server) do just that. I know Go has Arena API but am not sure regarding its current status. Are there newer/better alternatives?
- lossolo 2y ago> binary-trees benchmark is using Go 1.23.0 which is the latest version. Go submissions do not do anything differently compared to other GC-based languages for the most part. The fastest Java submission has interestingly much lower execution time than other Java submissions, and I plan to look into it, but that would be pretty much the only caveat. I think it is relevant given the topic of the discussion is GC implementations and their characteristics. I think what you are still missing is the context that I've added in my last two replies. This is not the use case that I'm using Go for. My micro benchmark was only to refute your claim that it's hard for Go to do tens of millions of allocations per second in a multithreaded environment. We are not CPU bound, we are IO bound, and we need low latency (in p95 and p99 too), low memory usage, and good throughput for our network services. We get all of that from Go. Would we get more throughput from Java or .NET Core? Probably, but what would we need to trade off? Memory usage or latency? Or both? Go is optimized for latency (not throughput) and always was. That works for us. > Regarding highly-loaded networked services with predictable or hand-tuned data lifetimes, in C# you are expected to completely bypass the allocations with the use of built-in ArrayPool<T>, stack-allocated buffers or NativeMemory.Alloc/Free which directly call into malloc/free. This is made possible by wrapping such buffers into Span<T> or Memory<T>. Things like Kestrel (web server) or Garnet (Redis server) do just that. The last time I used Java was probably 10 years ago. I was interested in C# when they introduced .NET Core with the possibility to compile it to a single binary and support for Linux. I actually got excited about Span<T> when they announced it. I'm not sure what the current status of Kestrel is, but back then (a few years ago) its latency, memory usage, and throughput were not so good. Garnet looks very good on paper. I added it to my bookmarks when they announced it. For now, we are happy with Redis, but maybe we'll consider Garnet one day. I just googled some more recent real-life workload benchmarks and I see Go still does a lot better there than C#. It's probably not a super scientific benchmark. And benchmarks are just benchmarks especially micro benchmarks: https://www.wwt.com/blog/performance-benchmarking-bun-vs-c-vs-go-vs-nodejs-vs-python https://www.wwt.com/blog/performance-benchmarking-bun-vs-c-v... Scroll down to http-server: https://programming-language-benchmarks.vercel.app/go-vs-csharp https://programming-language-benchmarks.vercel.app/go-vs-csh... etc. > I know Go has Arena API but am not sure regarding its current status. I wouldn't use it in production unless you know how it's implemented and you are careful. The current implementation probably will not get into the language as it's on hold. EDIT: It seems my love story with .NET Core, which began in 2016, didn't work out back then after all! https://news.ycombinator.com/item?id=12358370 https://news.ycombinator.com/item?id=12358370