| > Let's be honest: C is no "closer to the metal" than other high level languages This is dead wrong, and your links do not support it. This is exactly the kind of statement that gets me grumpy. Your link illustrates that an aggressive C optimizer can collapse a chunk of C code down to something smaller and simpler than the original code. This is true. But what you said is that C is "no closer to the metal" than other high-level languages. Let's examine this assumption. Take this C function: int plus2(int x) { return x + 2; }
You can compile this down into the following machine code on x86-64, which fully implements the function for all possible inputs and needs no supporting runtime of any kind: lea eax,[rdi+0x2]
ret
Now take the equivalent function in Python: def plus2(x):
return x + 2
In CPython this compiles down to the following byte code: 3 0 LOAD_FAST 0 (x)
3 LOAD_CONST 1 (2)
6 BINARY_ADD
7 RETURN_VALUE
Notice this is byte code and not machine code. Now suppose we wanted to compile this into machine code, could we get something out of it that looks like the assembly from our C function above? After all, you are claiming that C is "no closer to the metal" than other languages, so surely this must be possible?The tricky part here is that BINARY_ADD opcode. BINARY_ADD has to handle the case where "x" is an object that implements an overloaded operator __add__(). And if it does, what then? Surely just a very few instructions of machine code will handle this case, if C is "no closer to the metal" than Python? Well __add__() can be arbitrary Python code, so the only way you can implement this BINARY_ADD opcode is to implement an entire Python interpreter that runs __add__() in the overloaded operator case. And the Python interpreter is tens of thousands of lines of code in... C. The end result is that writing the same function in C and Python is the difference between two machine code instructions and implementing an entire interpreter. This is why I get grumpy when people deny that C is any different than other high-level languages. While this is a somewhat extreme case, you could make a similar argument about most operations that happen in other high-level languages; similar constructs will very frequently have less inherent cost in C. |
Haskell is only slightly less prone to this, despite being compiled, since every expression by default becomes a request to instantiate a thunk in the runtime's evaluation tree. Yes, you can contort yourself to avoid this, but at that point you are imperatively programming an expression tree evaluator. This can be fun and rewarding, but it's not that different from scripting a Python interpreter, and it's certainly much further from the metal than C.