In my experience, languages like Ruby and Python are slower than languages like Javascript, which are slower than languages like C#/Java, which are slower than languages like C++/Rust, which are slower than languages like C and Fortran. Assembly isn't always the fastest approach these days, but well-placed assembly can blow C out of the water too.
The ease of use and maintainability scale in reverse in my experience, though. I wouldn't want to maintain the equivalent of a quick and dirty RoR server reimplemented in C or assembly, especially after it's grown organically for a few years. Writing Rust can be very annoying when you can't take the normal programming shortcuts because of lifetimes or the borrow checker, in a way that JIT'ed languages allow.
Everything is a scale and faster does not necessarily mean better if the code becomes unreadable.
The extensive compile-time metaprogramming facilities in C++ give it unique performance advantages relative to other performance languages, and is the reason it tends to be faster in practice.
For example, compare the speed and implementation of std::sort and qsort (it's almost an order of magnitude difference in run time for big N!)
Also, sorting is something where algorithmic improvement makes a sizeable difference so you need to be sure you're either measuring apples vs apples or that you've decided up front what your criteria are (e.g. lazy people will use the stdlib so only test that; or nobody sorts non-integer types so I only test those)
For some inputs if you're willing to use a specialist sort the best option today is C. If you care enough to spend resources on specialising the sort for your purpose that's a real option. Or alternatively if you can't be bothered to do more than reach for the standard library of course Rust has significantly faster sort (stable and unstable) than any of the three C++ stdlibs. Or maybe you want a specialized vector sort that Intel came up with and they wrote it for C++. Hope portability wasn't an issue 'cos unsurprisingly Intel only care if it works on Intel CPUs.
Sure, if you write all the code. If you're writing a library or more generic functions, you don't have that power.
And, even then, while you can do this it's going to be much more code and more prone to bugs. C++ is complex, but that complexity can often bring simplicity. I don't need to specialize for int, double, float, etc because the compiler can do it for me. And I know the implementation will be correct. If I specialize by hand, I can make mistakes.
In addition, this isn't something where C "shines". You can do the exact same thing in C++, if you want. Many templates have hand-rolled specializations for some types.
> apples vs apples
It is, they're both qsort. When every single comparison requires multiple dereferences + a function call it adds up.
> For some inputs if you're willing to use a specialist sort the best option today is C
I don't understand how. Even if this is the case, which I doubt, you could just include the C headers in a C++ application. So, C++ is equally as good of a choice + you get whatever else you want/need.
> Rust has significantly faster sort (stable and unstable) than any of the three C++ stdlibs
Maybe, but there's a new std::sort implementation in LLVM 17. Regardless, the Rust implementations are very fast for the same reason the C++ implementations are fast - encoding information in types at compile-time and aggressively inlining the comparison function. Rust has a very similar generic methodology to C++.
Oh! No, that's not a thing. What's happened there is you saw that the libc function was named qsort and you went "I am smart, I know that means Tony Hoare's Quicksort algorithm from the 1960s" but that's not what it means, it is named that way but it's only defined as an unstable sort, the libc does not promise any particular algorithm.
Over in C++ land they also don't specify the sort algorithm used but in C++ 11 they mandated that the provided function must have worst case O(n log n) performance. This is awkward for Quicksort because although Tony's algorithm is very fast on average, its worst case is O(n squared) which is very slow
Thus, conforming C++ libraries are definitely not a Quicksort. Now, conformance to the C++ ISO standard is basically a minor curiosity and nobody cares, so Clang for example just didn't bother and shipped a Quicksort anyway until relatively recently, but already we can see that we're by no means guaranteed these are "both qsort" nor that they're both anything in particular.
The thing you should do is an introspective sort or "Introsort". There are a lot of these, for some time the best general purpose algorithm was PDQsort, the Pattern Defeating Quicksort by Orson. But even though that word "Quicksort" is in there this is not just "Well it's qsort so it's the same anyway" any more than a Cayenne is the same as a road legal 911 is the same as Porsche's 963 track car.
Yes, but not if you pass in void *. For libraries this matters. If you're both writing the producer and consumer then sure, you can do it manually.
> code bloat caused by monomorphization
This is true and a real problem, but I would argue in most scenarios extra codegen will be more performant than dynamic allocation + redirection. Because that's the alternative, like how swift or C# or Java do it.
Right, but then I'd have to write C++. Shallow dismissal aside (I really do not enjoy writing C++), the bigger issue is safety: I am almost certain to write several exploitable bugs in a language like C++ were I to use it to build an internet-facing web app. The likelihood of that happening with Rust, Java, C#, or any other memory-safe language is much lower. Sure, logic errors can result in security issues too, and no language can save you from those, but that's in part the point: when it comes to the possibility of logic errors, we're in "all things being equal" territory. When it comes to memory safety, we very much are not.
So that pretty much leaves me with Rust, if I've decided that the memory footprint or performance of Java or C# isn't sufficient for my needs. (Or something like Go, but I personally do not enjoy writing Go, so I wouldn't choose it.)
> Everything is a scale and faster does not necessarily mean better if the code becomes unreadable.
True, but unreadable-over-time has not been my experience with Rust. You can write some very plain-vanilla, not-"cleverly"-optimized code in Rust, and still have great performance characteristics. If I ever have to drop into 'unsafe' in a Rust code base for something like a web app, most likely I'm doing it wrong.
Very well summed. I'll remember this exact quote. Thank you.
Even the basics, nobody is calling Rust's [T]::sort_unstable without knowing it is an unstable sort. Even if you've no idea what "stability" means in this context you are cued to go find out. But in C++ that is just called "sort". Hope you don't mind that it's unstable...
[Edited because I can't remember the correct order of words apparently.]
You probably want the apples-to-apples comparison but this looks an artificially limiting comparison; people are shilling, ahem, sorry, advocating for their languages in most areas, especially web / API servers. If somebody is making grandiose claims about their pet language then it's very fair to slap them with C++ or Rust or anything else that's actually mega ultra fast.
So there's no "better" comparison here. It's a fair game to compare everything to everything if people use all languages for the same kinds of tasks. And they do.
perhaps you get 1300MB to 20 MB with C# or Java or go, and 13MB with rust . Rust’s design is not the reason for bulk of the reduction is the point
“Less languages features, but a better compiler” was originally the aspirational selling point of Go.
And even though there were some hiccups, at least 10 years ago, I remember that mainly being true for typical web servers. Go programs did tend to use less memory, have less GC pauses (in the context of a normal api web server), and faster startup time.
But I know Java has put a ton of work in to catch up to Go. So I wonder if that’s still true today?
I don’t think Java has any edge when it comes to deployment.
This contrasts significantly with effort and adoption of NativeAOT in .NET. Well, besides CLI, scenarios where it shines aren’t those which Go is capable of addressing properly in the first place like GUI applications.
I remember this being true 10 years ago. Java web servers I maintained had a huge problem with tail latency. Maybe if you were working on a 1 qps service it didn’t matter. But for those of us working on high qps systems, this was a huge problem.
But like I said, I know the Java people have put a ton of work in to try to close the gap with Go. So maybe this isn’t true anymore.
Go tends to perform better at "leaner" microservices, but if you are judging this only by comparing it to the state of Java many years ago, ignoring numerous alternative stacks, it's going to be a completely unproductive way to look at the situation. Let's not move the goalposts.
Hazelcast has a good blog [0] on their benchmarks between 8 and some of the more modern runtimes, here is one of their conclusions:
> JDK 8 is an antiquated runtime. The default Parallel collector enters huge Full GC pauses and the G1, although having less frequent Full GCs, is stuck in an old version that uses just one thread to perform it, resulting in even longer pauses. Even on a moderate heap of 12 GB, the pauses were exceeding 20 seconds for Parallel and a full minute for G1. The ConcurrentMarkSweep collector is strictly worse than G1 in all scenarios, and its failure mode are multi-minute Full GC pause
[0] https://hazelcast.com/blog/performance-of-modern-java-on-dat...
Are you hiring developers that are 100% fully conscious of concurrency and starvation or people that are only concerned with rest and vest and TC?
For either case Go is better.
* For people that are aware of concurrency, they will select Go because they appreciate its out-of-the-box preemptive concurrency model with work stealing.
* For people that are not aware of concurrency, then you should definitely use Go because they are not qualified to safely use anything else.
A faster compiler was the aspirational selling point. As legend has it, Go was conceived while waiting for a C++ program to compile.
Before what was called "Go 2" transitioned the project away from Google and into community direction there was some talk of adding no more features, instead focusing on improving the compiler... But since the community transition took place, the community has shown that they'd rather have new features.
The "Go 1" project is no longer with us (at least publicly; perhaps it lives on inside Google?)
Also you can do that same thing in Rust or C++ too. Very common in C++, speeds up programs quite a bit.
CoreCLR itself doesn't take much memory - GC might decide on a large heap size however. Do give .NET 9 a try with Server GC which has enabled DATAS by default. It prioritizes smaller memory footprint much more heavily and uses a much more advanced tuning algorithm to balance out memory consumption, allocation throughput and % of time spent in GC.