* Strings. Any form of string access other than scanning the string's characters one by one from start to finish can benefit from knowing the string's length in advance. In C++, std::string and std::string_view know their lengths; plain C strings don't. Thus, in performance-optimal plain C, almost any function that takes a string parameter ought to also take the string length as a separate parameter - but most C standard library functions neglect to do so.
* Callbacks. When the callback is static, you want to give the compiler the opportunity to inline it into the call site. In C++, this is natural: pass the callback as a template parameter. In performance-optimal C, you'd want to provide macro version of functions that take callbacks. But the C standard library only supports callbacks as function pointers (e.g. in bsearch(3), qsort(3) etc.), which means unnecessary pointer dereferences and which makes inlining impossible.
But.....there is no such thing. There are arrays of chars, the whole principle of C is "if you want to know the length of a string....just store it yourself". It's a bit like saying that two wooden planks don't have the same functionality as a cupboard. Like, you're technically correct, but the whole idea is that you can use the planks to build your own cupboard or literally anything else.
C++ makes certain intentions clear to the compiler in ways that C doesn't, which makes certain optimizations possible.
https://stackoverflow.com/questions/18002087/qsort-vs-stdsor...
Or the CTL is a header only STL for C, which inlines the sort function and the comparator. And because of the C++ bloat and indirect calls the C version is faster.
Inlining with macros == constexpr with templates.
You can decide which syntax you prefer.
And yet, rather than ignoring it, you are commenting on it, with a pithy retort which dismisses the entire benchmark without actually providing any additional insight.
Programming languages, compilers, library ecosystems, the groups of people who decide to sit down and try to produce a better result for a given language, and the benchmark maintainers who decide what submissions count for a given language (does a C solution that just uses entirely inline ASM count?) are incredibly complex systems. Any single metric is never going to capture the full richness of the language, is never going to be representative of the experience you will have for every single program, etc.
But does that make metrics useless? No, it just means that you should be informed about their limitations. You shouldn't just look at a single number, but instead make sure you understand well enough what is being measured to know how well that number represents anything useful.
So rather than just dismissing this benchmark, it would be useful to ask "why are the C++ results better than the C results on this benchmark?"
Some benchmark challenges like this allow pretty much any program that accepts the right input and produces the right output; which means you get results in which no computation is actually done, the output is simply hard-coded and you are basically just measuring the startup time or request time of the language or library.
This particular set of benchmarks imposes some constraints to avoid that kind of behavior. Programs have to follow the same basic algorithm, so you can't figure out some clever algorithmic optimization which applies only for the particular input used in this benchmark. For things like the regex challenge, you are expected to use either the built-in regex in your languages standard library, or a common general-purpose regex implementation, not a specialized regex implementation optimized just for this particular benchmark.
The goal of this set of benchmarks is to provide a reasonable set of reasonably realistic small problems, implemented using the same algorithm, and using the normal language and library features. It uses small simple problems in order to make it easy to read the programs and learn about the performance characteristics of the language.
So rather than dismissing, why don't we take a look at the fastest C and C++ implementations of some of the problems?
Here's the fastest implementation of the k-nucleotide problem in C and C++:
https://benchmarksgame-team.pages.debian.net/benchmarksgame/... https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
I haven't sat down to do detailed profiling and performance comparison of each; but just off the top of my head, here are a few things I see which could be relevant.
The C++ implementation takes advantage of a number of C++ features; it uses a hash table from GNU pb_ds, a C++ container library which allows for a good amount of customization based on template parameters. The C implementation uses khash, a very fast hash table implementation which uses macros for similar customization.
The C++ implementation makes note of using move semantics in a number of places, which potentially allows for certain optimizations that wouldn't be possible if the compiler had to copy data.
The C++ implementation uses insertion into a templated ordered map to sort the results, while the C implementation uses the standard library qsort. This allows the comparison function to be inlined into the C++ sort, while it's called through a function pointer in the C implementation.
Without actually doing some experimentation and profiling, it's hard to say which of these makes a difference, or if it's something else. But this does show that C++ provides facilities for generic container and algorithm types that C does not. In the C implementation, macros are used to work around this for the hash table case, while function pointers are used for sorting.
Anyhow, rather than simply dismissing these results, why not dig into where the difference really lies, and provide a better implementation in C if you think that you can?
No one set of benchmark results should be taken as gospel. But I think this particular benchmark game is fairly useful for getting a rough sense of "if I write all of my code in this particular language, using either the standard library or commonly available off the shelf libraries, how much of a performance penalty am I likely to pay?"
I also find that the grouping of languages that he does, based on the minima of the kernel density estimation of their geometric mean scores, to be a bit more informative than absolute ranking within those groups. That gives a sense of the general class of languages. There's one group for C, C++, and Rust; languages which allow for performance without compromise, at the expense of lack of safety, higher complexity or learning curve, or both.
There's a next big group with a lot of languages; most of them have been around for a while, or been designed with an eye towards performance, but still have some amount of overhead due to GC or pointer chasing or greater thread synchronizaiton overhead or any number of other reasons; this group includes Fortran, Ada, C#, Java, Go, Haskell, etc.
Then there are a few groups of fairly high-level, dynamic languages, designed for scripting or rapid development, and which require you to trade off a fairly significant amount of performance for this. Dart, PHP, Python, Erlang, Ruby.
And finally, there's Matz's Ruby, all alone in a group at the end, slower than pretty much everything else. I'm not quite sure why it's separated out from Ruby, which seems to refer to yarv, but maybe it's so people who come here wondering what they can do about their slow Ruby programs can see that they can at least get a big boost by switching to yarv.
Anyhow, this benchmark and this grouping helps if you're considering what to do about some performance bottleneck you have in some code base; or if you're starting a project for something which will potentially be performance critical. Moving between languages in one group isn't all that likely to make a substantial difference; but moving to a language in another group would. For example, it lets you know that there's a pretty good chance that just rewriting a Python program that's a performance bottleneck in Go would improve that performance; but rewriting a Go program in Java, or vice versa, is less likely to be a performance win.
By the way, YARV has been "ruby" since 2007; these benchmarks were run with ruby 3.0.0preview1. The name has probably just never been changed.
Thanks for the kind words, not sure why it's getting downvoted other than that I called someone out on their lazy dismissal. But I wouldn't worry about it too much, I've been on HN and Reddit for long enough to not care too much about downvotes.
As a marker of where Ruby used to be.
These kinds of comments, and downvotes in place of discussion, are corrosive to honest, informative discussion, and I've seen way too many tech discussion communities get taken over by people who make low-effort dismissive comments and avoid engaging in actual substantive discussion.
> … designed with an eye towards performance, but still have some amount of overhead…
Here's a rough count of the programs contributed to the benchmarks game since April 2018.
Does this information moderate your opinion?
Julia 48
Rust 35
F# 23
C# 22
Go 22
Node 19
C++ 16
Haskell 15
Pascal 14
Chapel 11
Swift 10
Lisp 9
Python 9
Dart 7
OCaml 6
C 5
Fortran 5
Erlang 4
Java 4
TypeScript 4
Lua 2
Perl 2
Racket 2
Ruby 2
Ada 1
PHP 1