Thoughts on Runtime Efficiency of Software

A rambling about how the software industry has been preferring to pay the overhead for runtime and memory safety, in contrast with C programming, which is bare bones and mostly has no runtime overhead.

Lately I've been interested about programming in C to create fast and efficient programs with minimal runtime complexity.

Virtually all high-level languages today impose a runtime overhead that has little to do what you actually want the program to accomplish.

For example, Python runs on a Virtual Machine which interprets bytecode. To run a single Python instruction, it takes multiple processor cycles. The dynamic typing also impose runtime overhead, since the type of the variable must be checked to properly run any native code on it.

Java boasts Ahead of Time (AoT) compiling, which converts the Java bytecode into native instructions, but that takes some time. The Java Virtual Machine famously takes some time to spin up and can be taxing on RAM usage as every class file must be loaded on memory before any code is run.

JavaScript, paired with the V8 engine by Google, has a quite impressive performance. It can near native speed thanks to very clever native code generation and runtime type handling.

What all these runtimes and Virtual Machines have in common is that they are written in C (or C++). They are compiled to the lowest level a program can “talk” to a computer, native machine code, targeting an specific processor architecture. (x86, x64, ARM, and so on).

The processor already has a huge complexity built-in to achieve incredible speeds, but most of it is swept away when ease of development is chosen in favor of runtime efficiency.

Now, C isn't without its failures. Buffer overflows, the lack of memory-safety and manually managed memory allocation all plague software written in C and are a constant source of bugs and vulnerabilities, even on large-scale software projects.

These shortcomings are addressed by more modern languages with runtime bounds-checking, a managed memory model and automatic garbage collection. All of these are great ease of development, but force the program to keep track of more things during execution.

All of these trade offs aren't new or have a clean solution. Lisp was born ages ago and, as a scripting language, without even looking it up, I can bet that it drew the same criticism as I'm mentioning here.

I guess compiled/native vs interpreted/managed will always be worth considering when taking on a new software project, with the balance pending to the second during these days with the dominance of web technologies.