That said, two things:
1. Go is not particularly efficient as a language and its compiler doesn't optimize much compared to the other "efficient" language compilers (even the JIT ones).
2. These small benchmarks benefit JIT languages quite a bit since the JIT cost is barely noticeable. In large real-world apps constantly running a compiler in the back is not exactly efficient. It'll pay for itself vs interpreted languages, but never against AOT compiled ones.
I mean, relative to many other mainstream languages including many of the JIT languages, Go is very efficient, but a lot of that efficiency comes from the language design (e.g., idiomatic value types) such that the compiler and runtime don’t need to be as smart.
Agreed that the benchmarks game is a pretty crumby measure of performance, but it typically imposes strange restrictions on Go that prevents it from showing its idiomatic performance (never mind its ceiling). For example, languages with garbage collectors aren’t allowed to preallocate memory for certain benchmarks even though that’s absolutely idiomatic in Go (because Go has value types) so you have languages with GCs optimized for languages where everything is an allocation which perform well and languages like C and Rust which are allowed to preallocate because they don’t have a GC (which is a weird criteria) and then you have Go which appears to be very slow because it’s forced to allocate in a tight loop.
From a language design perspective (when it comes to "ease of optimization") it's much better than Java and comparable to C#.
But compiling C# with CoreRT (which is basically .NET Core's RyuJIT configured as an AOT compiler) shows just how little Go's compiler optimizes. Go would need to be even more "easy to optimize" than it is to make up the difference. Rust and C++ are both easier to optimize and their compilers are in a whole other league entirely, and it shows on the horrific compilation speeds.
I'm not sure what the right way to do these "benchmarks" is. Some of the code I've seen there was more low level and convoluted than stuff I'd write in C. Not even remotely representative of how the languages are used. Stupid rules like that one you mentioned also don't help since some languages get to preallocate and others don't.
In which case, maybe what you see is as good as you'll get.
> … some languages get to preallocate…
Here's Go using a library pool —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Because as-you-know the whole point is to "allocate zillions of short-lived nodes" —
The Swift Code looks like something you'd write in C.
Like this? (There are 8 other C++ binary trees programs.)
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
> … or Swift's reference counting.
Like this? (There are 4 other Swift binary trees programs.)
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
You could look at the different binary trees programs shown on the current benchmarks game.
Most people aren't building applications whose business requirements preclude preallocating memory except for languages which lack garbage collectors.
If we don't experience a performance problem, we don't need a workaround.
Errare humanum est.
"Energy Efficiency across Programming Languages" not "programming language performance in the general case".
I maintain a contrived benchmark is not a useful benchmark.
In this case for Go, the same as other languages that provide a library pool implementation.
Apparently the study authors found a use.
You are looking in the wrong place.
Here are the 4 year old programs used in the "Energy Efficiency across Programming Languages" study —
https://github.com/greensoftwarelab/Energy-Languages
> … nobody would write code like that…
When you tell us you wouldn't, we should all believe you.
When you tell us nobody would, we should ask how you know.
Well, I randomly went and opened the C# binary tree example. I see a TreeNode struct with a private Next class inside of it. Can't think of any reason to write a BinaryTree like that in C# beyond trying to game some benchmarks.
The C++ implementation uses a node pool from the Apache Portable Runtime (a C library). Also not idiomatic (how did they get away with using a library?!). I particularly love how it doesn't even use std::string to write text to.
The Swift implementation also uses the Apache Portable Runtime node pool, completely non-idiomatic (even less justifiable than the C++ one), don't need to go into detail on this one.
The Go and Java implementations look perfectly normal for the respective languages. The Dart one too, but that one doesn't even use any concurrency so it's starting from a losing position.
This is not a fair comparison.
Did it work?
If it wasn't massively more performant than other ways to write that program in C#, then maybe it's just fine for the purposes of "Energy Efficiency across Programming Languages".
> … but that one doesn't even use any concurrency…
What is the relationship between multicore and energy efficiency?
> This is not a fair comparison.
The comparison is energy efficiency not elapsed seconds, yes?