The Embedded Software Engineer’s Primer: Compiled vs Interpreted Languages

When observing modern programming languages you can generally group them into one of two categories. Compiled or interpreted.

In actuality there is a bit of a blurry line between what is considered a compiled language or an interpreted language. It’s not entirely black and white. For instance, Java toes the line by being compiled into Java bytecode but still executing on the Java Runtime Environment? It’s weird… Python code can also be compiled! However, this article will discuss the differences between compiled and interpreted languages as if they’re distinct.

As we discussed in the previous chapter of The Embedded Engineer’s Primer, all software run on a modern computer will eventually be translated into the language which processors understand directly; assembly.

The idea of a compiled language is easy. We write our code in C (Or some other compiled language). We compile that code using a compiler (For C, that is usually the gcc compiler). The resulting assembly instructions are saved to a file. That file is then executed by our processor. Done.

There’s a bit more captured in an executable file other than simply the instructions of the program. One of these days I’ll break down the anatomy of an executable file! Today is not that day.

But what is an interpreted language?

An interpreted language is one in which the source code you write is not directly the source code that is being executed by the processor. You never create a new executable file. Instead, you produce a text file in your chosen language, then provide that text file to an executable (That language’s runtime environment). That executable parses your text file in real-time and then, on your behalf, performs the behavior you requested of the processor after reading each line. For python, that executable is called “python” (obviously).

Let’s go back to our Python “Hello, World!” example program from before. However, this time let’s create a file called, myFirstProgram.py and the entirety of that file’s contents will be this one line:

print("Hello, World!")

In order to execute this program, we need to call python and provide it our file by executing the following in a command line:

python myFirstProgram.py

The result of running this program will simply be the text “Hello, World!” Notice, python itself is an executable that is parsing your myFirstProgram.py file. If myFirstProgram.py was larger and more than just one line, it would execute line 1 before ever reading line 2.

In short, that’s the difference between a compiled and interpreted language. A compiled language is one in which the software you write is directly translated into assembly language and is itself being executed by the processor. An interpreted language has a separate runtime environment. That runtime environment is the executable that is running on the processor. The source code you write is being read and “interpreted” by this runtime environment line by line. Your code simply requests the runtime environment to perform the written behavior on your behalf.

Interestingly enough, the python runtime environment is a compiled executable written in C! That fun fact doesn’t actually mean anything for this conversation but… It is interesting. Maybe that’s another discussion we can have about just how pervasive the C programming language actually is. C is so important to software, that even C is written in C!🤯

Now that we’ve defined what an interpreted language is and how they are different from compiled languages. Let’s discuss the several natural questions that you must be thinking about.

  • Why do multiple languages even exist?
  • What are the advantages/disadvantages of compiled vs interpreted?
  • Which language should I use?!

A natural question that many beginner developers have is why are there SO MANY programming languages? If they’re all just listing commands for a processor to execute, why have we not all just agreed that <insert your favorite language here> is the one true language that we should use for EVERYTHING!

As weird as it may sound, there’s a lot of validity to this question. Because, for the most part, any problem which you could possibly imagine is capable of being solved by essentially any programming language. It might be really difficult to do, but it’s likely possible!

Before answering this question, let’s take a step back and comprehend what problem a programming language attempts to solve. All modern programming languages exist to ease the process of human beings explaining problems to a computer. All processors can only perform a short list of tasks. They can add, subtract, store memory, etc… Programming languages simply ease the process of translating abstract concepts that are easy for humans to understand like classes, functions, variables, loops, etc… into a large collection of precise tasks that a processor is capable of executing. There is a massive trade-off between how precise that translation can be versus how easy a programming language is for humans to write. Generally speaking, the easier it is for a human to write behavior in a language, the less efficient the language will be at translating that behavior into instructions on a processor. Different languages make different trade-offs. Python for instance is one of the more simple languages to read and write for humans. However, it is notoriously slow to execute when compared to the same behavior written in C. Python is still plenty fast for simple programs but the more complex your application becomes the more the inefficient performance of python compounds. If you have a large project and strict performance requirements, maybe Python isn’t your best choice.

Although, the problem with C is that it is notoriously complicated. Just look at what it takes to write the simplest “Hello, World!” program in C!

#include <stdio.h>int main(int argc, char** argv) {
printf("Hello, World!\n");
return 0;
}

There’s a lot in this source code that doesn’t immediately make sense unless you already understand the C language. Like what is this “int argc, char** argv” nonsense? And why return 0?! But, when you want to write something with strict performance requirements, (Like a video game engine!) you are going to want to use a language that provides the level of control and precision that C or C++ provides.

Each new programming language is usually born from the need for an easier way to translate what humans understand into what computers understand. As we continue to learn about computers and learn how to communicate with people about computers, the vernacular we use changes. And thus, the languages themselves change to ease conveying these new ideas to computers! For example, Apple’s new Swift language was born from the need to more easily convey the ideas required to solve the problems of a modern touch-screen application. Touch screen applications just plain didn’t exist when the original language Apple used, Objective-C, was created. The problems of the future are constantly changing, so the languages we use to describe these new problems are constantly improving. Also, just because a new language comes out does not mean that the old problems or the people who are more comfortable using the old languages immediately go away! And similarly to how it’s difficult to get the whole world to speak English, it’ll be difficult to get the entirety of the software community to use Python. Even though I personally think they should.

Keep in mind that as the software community learns more and more about how to best write software, the gap in performance between compiled and interpreted languages is dwindling. Python today is WAY faster than the original python. We’re all still figuring this out! Software as a whole has only existed for ~60 ish years. New problems keep needing new solutions and new languages are constantly improving upon old ideas.

  • Portability.

Portability is an advantage of interpreted languages because the same exact source code, without modification, can be run on any operating system which supports the runtime environment. You can be reasonably assured that you can write your code once, then be able to run it nearly everywhere! That’s powerful.

  • Simplicity.

Because the runtime environment handles all of the correspondence with the operating system and the hardware on your behalf, the source code of an interpreted language is capable of being far more simple to read and understand than a compiled language. Simplicity is valuable because it eases the process for people to learn how to be productive in a new language quickly! And with that ease of transition usually comes a larger group of developers actively using the language. Which brings us to the next advantage.

  • Community.

A large community of developers breeds a healthy environment to solve problems within. The software community is generally quite giving. Many complex solutions to problems are written as free open-source plugins available for you to use in your own projects with free licensing! The larger the community, the more open-source solutions there are for you to build your own projects with. There will also be more 3rd party APIs available and you’ll find more search results when you inevitably need to use Google for help! Writing software, although possible to do alone, is better when done as a team. If you’re a business building your team, you’ll want as large of a pool as possible to choose candidates from.

  • Performance.

As I’ve mentioned before, interpreted languages are generally less efficient at translating the source code you write into instructions on your processor. The mere fact that your source code needs to be read at run-time is itself a hindrance to performance. However, if performance is not a primary requirement then perhaps this disadvantage isn’t very meaningful.

  • Lack of Control/Hardware Access.

Another disadvantage is that because interpreted languages aim to be more simplified, they tend to remove the capability to reference specific locations in memory directly. Depending on your project, this could be a limiting factor. There are ways to get around this, usually by creating a custom plugin in C and then calling it from Python, but doing so isn’t always an efficient use of time.

  • “Magical” behavior.

The last disadvantage we’ll discuss is more of a personal gripe that I have with using many interpreted languages for production quality projects. The amount of behavior that is abstracted away from you as you write your source code can be difficult to understand in some languages. Due to this lack of understanding of what is happening on your behalf, debugging issues can be difficult. For example, Javascript and web plugins in general fall into this trap often. The frameworks built on top of Javascript perform lots of complex behavior often without explaining what’s actually happening behind the scenes. Debugging is hard when you don’t know what your code is doing.

  • Speed.

Compiled languages generally run much faster than interpreted languages and are more predictable because you generally have to painstakingly explicitly write all your desired behavior. Especially if you’re writing the software for something with high consequences for failure, like a pacemaker or a vehicle. You will want to explicitly know everything that’s happening and when it’s happening.

  • Direct Access To Memory/Hardware.

As described earlier. Many interpreted languages completely abstract the hardware away from you so if you require direct access to memory or hardware registers then you may be forced into using a compiled language.

  • Less “magic”.

Again, this notion of “magical” behavior is a personal preference. As you may be able to tell by my writing, I enjoy being verbose! Likewise, I enjoy the lack of ambiguity of a compiled language.

  • Stability/Maturity.

Compiled languages are generally much older and thus more stable and mature than many interpreted languages. C for instance has been largely the same for decades and continues to be widely used.

  • Lack of Portability.

In order to move between operating systems, your compiled code will usually need to change. Different compilers tend to be slightly different from each other and different operating systems have different requirements that must be satisfied by your source code in order to operate. Generally speaking, if you want your code to run on a wide variety of Operating Systems/hardware, interpreted languages are the most time-efficient option.

  • More obscure problems.

Compiled languages, when they fail, often are harder to debug and research what caused the fault. Although more predictable, compiled languages have to deal with errors that are not easily traceable. Specifically, segmentation faults or issues with dereferencing an unintended memory address are difficult errors to debug because neither the compiler nor your operating system/hardware can tell you that you will reference the wrong memory address until you begin executing. You’ll end up with just vague address out of bounds errors or the subtle behavioral differences that come along with using unintended values. Finding out what went wrong can be frustrating. It happens to the best of us…

Unfortunately, I can’t just give a direct answer on which language or which type of language you should use without having an understanding of the problem you’re attempting to solve. For the most part, popularity is the largest determining factor when I choose a new language for myself. Whichever language the most people use to solve similar problems to the problem I’m attempting to solve will always be my top choice. Because with popularity comes community and communities drive the creation of information and resources. The second most useful factor in my opinion is how well the documents of a language are organized. If I can navigate the official documentation for a language easily then that’s a huge bonus in my eyes.

Overall, your choice of language doesn’t really matter all that much. What matters is your ability to create what you set out to create! If you’re ever uncertain about which way you should go, just choose something. Anything! Just starting is more important than wasting a ton of time figuring out how to start.

Research Engineer — Bored Millennial

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store