|
> Literally any language that does the same will be the same speed but not faster, because there's no way to be faster. It's physically impossible. There is nothing special about C that makes this true. C has semantics, just like any language, that are higher level than assembly, and sometimes, those semantics make the code slower than other languages that have different semantics. Consider this C function: void redundant_store(int *a, int *b) {
int t = *a + 1;
*a = t;
*b = 0; // May clobber *a if (b == a)
*a = t;
}
Because a and b may point to the same address, you get this code (on clang trunk): redundant_store:
mov eax, dword ptr [rdi]
inc eax
mov dword ptr [rdi], eax
mov dword ptr [rsi], 0
mov dword ptr [rdi], eax
ret
That fifth line there has to be kept in, because the final `*a = t;` there is semantically meaningful; if a == b, then a is also set to 0 on line four, and so we need to reset it to t on line five.Consider the Rust version: pub fn redundant_store(a: &mut i32, b: &mut i32) {
let t = *a + 1;
*a = t;
*b = 0; // a and b must not alias, so can never clobber
*a = t;
}
You get this output (on Rust 1.90.0): redundant_store:
mov eax, dword ptr [rdi]
inc eax
mov dword ptr [rsi], 0
mov dword ptr [rdi], eax
ret
Because a and b can never alias, we know that the extra store to *a is redundant, as it's not possible for the assignment to *b to modify *a. This means we can get rid of this line.Sure, eliminating one single store isn't going to have a meaningful difference here. But that's not the point, the point is that "it's not possible to be faster than C because C is especially low level" just simply isn't true. |
Yes, obviously different languages will produce different assembly based on the language semantics. So you will get performance differences. And it's certainly possible for code written in Rust or Zig or Odin to be more performant then C code depending on how it's written.
My point was about classes of languages (for lack of a better term). Loosely, from fastest to slowest you have:
1. Compiled languages (meaning straight to machine code) with manual memory management
2. Compiled languages with garbage collection
3. Languages that run in VMs but are AOT compiled or JITed
4. Purely interpreted languages.
I acknowledge that not all languages fit nicely into these and there will be exceptions, but it's a convenient mental model that's close enough for these purposes.
Languages in the first category are going to be the most performant. Obviously there will be some variation between them based on how the code is written, but unless it's written really poorly it's not going to drop into an entirely different category. Where as languages in other categories are going to be far more difficult if not impossible to get close to the same kind of performance.
And there is no meaningfully huge jumps left after the first group. We are all the way down at optimizing assembly code, and that's where you start to hit physical limitations. Some number of operations have to be executed and the CPU can only execute them so fast.