4 ms·
Low pauses, low memory overhead, low CPU overhead. In Java world, pick one. In C/C++/Rust/Zig you can have all three at once.
by pkolaczk 4y ago
Low pauses, low memory overhead, low CPU overhead. In Java world, pick one. In C/C++/Rust/Zig you can have all three at once.
- krzyk 4y agoAdd memory safety to the list. And allow to select only 3.
- pkolaczk 4y agoBut that's not performance category. And, btw, Rust has all 4 of them. If we're talking about safety guarantees then let's add data race freedom to the mix. Or use-after-free protection for non-memory resources. ;) Java is cool language and has many nice features, but its type system is not its major advantage.
- kaba0 4y agoMalloc and friends have much more CPU overhead than a modern GC, so if you do choose all of these, brace yourself to a really micromanaged programming.
- pkolaczk 4y agoThat's a common wisdom, but there is little if any evidence to support it. True GC costs are simply more hidden and harder to measure. A malloc/free pair might be slightly slower than Java's new (not by much really - go measure), but invoking `new` is not the only cost of GC. https://arxiv.org/abs/2112.07880 https://arxiv.org/abs/2112.07880 Also modern low pause GCs have way higher CPU overhead than the old STW ones. Try to set a low pause GC pause target to 0.1 ms or set the heap limit to 110% of live memory and see what happens to CPU consumption. Last time I tried, the app didn't even start - got OOM nearly instantly. There is a reason the study above measures at 3x oversized heap. The cost of GC is lower than malloc/free only if you give it a lot more memory than you actually need. Which in many cases is a good tradeoff to make, but it is good to know such tradeoff between memory and CPU exists. And finally there is one more thing that's not particularly a trait of GC, but rather a limitation of current Java - it is much easier to write a C++/Rust program with low number of heap allocations than in Java. The fact that malloc is slightly slower does not matter if you invoked it 10x less frequently.
- kaba0 4y agoYour point on low-latency GCs is indeed fair (read barriers vs write barriers), and I should have probably specified ref counting where the overhead is much more apparent (and is a fairer comparison). But regarding malloc-only, fragmentation also comes into picture which does have a non-negligable effect. And while Java does like to “heap”-allocate, it happens foremost on thread-local buffers and are used pretty much as an arena allocator. Even without escape analysis, these are very cheap all around.
- pkolaczk 4y ago> And while Java does like to “heap”-allocate, it happens foremost on thread-local buffers and are used pretty much as an arena allocator. Even without escape analysis, these are very cheap all around. If that was true, it would be fairly easy for Java to come close to C++, C, Rust, Pascal in the "binary trees" microbenchmark in The Computer Language Benchmarks Game. This microbenchmark is the one that stresses dynamic heap allocation, and is traditionally favoring bump-allocation, so compacting GCs should have an easy win, shouldn't they? The problem is: the best Java implementation loses this benchmark by far on the CPU part (~2.5x worse) and terribly on the memory part (~20x worse) when using the default stop-the-world GC. I attempted to run this benchmark using ZGC with OpenJDK 17, and here are the run times depending on the heap size: 250 MB: OOM 300 MB: 27.3 s 500 MB: 12.4 s 750 MB: 8.4 s 1G: 7.8 s 2G: 5.1 s 16G: 5.6 s For comparison, the best Rust program on the same computer runs in 0.8 seconds and takes about 150-280 MB of RAM (max RSS, varies from run to run). And the C version #5 that uses malloc (no arenas) is still about the same speed as Java at 2G: 5.4 seconds.
- kaba0 4y agoThe binary tree benchmark is made to stress test the GC algorithm, and Java beats out every single managed language by a huge degree. Low-level languages don’t do the same thing for this test, so I don’t really see the point of comparing C vs Java on this test - unless using raw memory buffers would be allowed for java as well.