Are Lower Level Languages Better?
Posted by Jake on Mar 15, 2013 in Programming
I dropped a link to the post on my programming history on Facebook and got an interesting comment from one of my lifelong buddies – none other than Digger Waggle:
I learned 6502 assembly when I was around 14 . Never really learned C or C++ or any of the higher level scripting languages(though I know how to read them like I can read a French or Spanish label on the back of the shampoo bottle.) I’m still of the strange mindset that the only way to really program is ASM.
I’ve often thought about the subject of this post, and Digger’s comment just brought it to the forefront of my mind.. Are Lower Level Languages Better?
My definitive answer is: Um, it depends? But more often than not, the higher level language will win out.
“Why”, you ask? Let’s start out with an abbreviated and paraphrased computer history lesson… On second thought, check out this article and then come on back for my explanation. I’ll wait here.
Ok – back? Did you get all that? Good. Read on…
You see, back in the day, modern computers simply were not very powerful. They had exceedingly limited computing power, almost no persistent (disk) or volatile (RAM) storage – and very little in the way of an interface to the world at large. Due to these limitations, the knowledge of the inner workings of the machine were mandatory in order for a programmer to do anything useful at all with it.
See the crazy thing above me? Yeah, that’s a computer punch card. And on it is written a very small portion of a computer program written in the lowest level language of them all – machine language! Each number in a column a bit, each column a byte. Each bit in that byte (punched being a 1, and not being a 0) would tell the computer to do something. Thousands of these cards would, in sequence, be fed into the machine for it to execute it’s program.
“You’re out of your mind”, you say? Well, that’s the way it was done. And you know what – it was ok back then. It took a special person with an extremely right-sided brain to go through the effort of punching out a useful program – but luckily computers weren’t really in wide scale usage. So the limited numbers of programmers wasn’t an issue.
Machines got faster, had persistent storage, actually had screens to see output and keyboards to handle input. The age of the computer was upon us and its adoption and usage accelerated. Computers were no longer an experiment – people actually wanted useful tasks done by them.
As you can imagine, coding in ML was painful, error prone, and took far too long. In an effort to speed up development an abstraction of the development language needed to be made – and assembly language was born.
Assembly worked on opcodes which corresponded to a bunch of machine code – much like higher level language subroutines, methods, etc. This abstraction allowed for much faster and less error prone development by utilizing generalization – at the cost of performance – be it processing speed, volatile storage, etc. Luckily in assembly language’s case it was still very near its origin and hence the overhead was minimal. Thousands of punch cards for a single program were now reduced to a fraction of the number of assembly opcode commands.
Computer processing speed and capabilities increased at a staggering rate. The computer was no longer just catching on, or a novelty – but a necessity. The workload they were asked to handle, and the amount of coding necessary to accomplish it, was ever increasing. No longer could developers be chained to the computer at such a low level. Although powerful, it was completely unnecessary. The developer needed to think about the functionality of the application and not so much about the inner workings of the machine and how to push bits around.
Assembly, at this time (for the majority of development) would no longer do. What was needed was yet another, higher level abstraction. Languages like C were evolving. Much like assembly opcodes corresponded to dozens of machine instructions, subroutines and functions now acted as dozens of opcodes.
Development speed – and complexity – increased dramatically. This same process continues today. The more the machine can handle, the more complex the problem it’s asked to solve. The more complex the task at hand, the higher the level (for the most part) of the language used to implement it must be in order to provide the most efficient solution possible – not just machine efficient, but everything that goes into the term – development speed, resources, bugs, speed to market, etc.
Each layer of abstraction, by its nature, sacrifices some level of performance due to generalization. But as computer speeds and capabilities increase, the need for machine efficiency decreases. There’s no longer the need to watch every bit or byte, there’s work to be done!
I’m not saying that there is no place for assembly. There most certainly is – any application that requires low level access or the highest performance are two such instances. But for the most part, in modern programming with deliverables and profit margins, there’s little room left for
cmp BYTE [eax + ecx], 0
Ahh, those were the days… Bleep blurp bleep