Are Lower Level Languages Better?


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.

Computer Punch Card

Computer Punch Card

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.

Moving on…

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.

Time passes…

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


Mar 31, 2013 at 9:09 am

Have to disagree with your statement that an assembly opcode corresponds to a “bunch” of machine instructions.

An assembler opcode corresponds to ONE machine instruction. Assemblers gave a way of coding machine instructions in a more readable way than opcode bits and bytes.

If you look at a 6502 assembler program listing, every line generates exactly one machine instruction. An assembler macro is a different story.

Mar 31, 2013 at 1:04 pm

Oh I agree, you’re 100% correct.

I was mostly generalizing so as to not delve into the bits, bytes, nibbles, words, etc…

Neil Harding
Mar 31, 2013 at 7:00 pm

It depends on the processor, I think I can write almost as efficiently in 68000 as I can in C, but 80×86 takes more cognitive processing because of it’s awful instruction set 😉 For programs that are intended for a one of use, I actually quite like Python for processing a file since you can write a useful program in 10 lines of code, but the drawback is the speed which is a fraction of the speed you’d get in C++ or assembly language. I used to mix code and data together when writing 68000, so you could have an animation with code in it, I had a small “language” so you could embed commands, I had 16 bit value for the frame index, with negative values being commands, if the value was negative it used bits to determine the command, so I had bit 1 = read word, and add it to x position, bit 2 = read word, and it to to y, bit 3, read word to get offset into the structure, read next to word to add that value, but 4, read word, and test value in structure, etc, so I could have loops, with simple conditions, and I also had read address of routine, in case I needed something more complex. That is hard to do efficiently in any of the high level languages.

Mar 31, 2013 at 8:22 pm

Hey Neil, thanks for the comment! I understand exactly what you’re saying as far as efficiency in coding – the lower level you code, the closer you are to the machine, the more efficient your code can be.

What I’m elaborating on is not the efficiency of the code, but the efficiency of the project as a whole. With the complexities required by the applications we write today it is imperative that the code is production ready as fast as possible – not just running as fast as possible.

Low level inefficiencies are covered up by ever expanding capacities of storage and processing – and products are moving to production, with an infinite number of more features, much fast than ever before.

As much as I appreciate the beauty of low-level code and the masters who code with it – I can say with utmost certainty that there isn’t a single guru coder in the world that can code today’s business applications in a low-level language faster than I can in any high-level language.

Project success isn’t measured in saved CPU cycles nowadays – it’s measured by how fast it gets out to be a value to the business sooner…

Neil Harding
Mar 31, 2013 at 8:51 pm

It depends on what you are doing as well, for file handling where you are doing string manipulation then higher level languages make it so much easier, but if you are interfacing with hardware say writing a device driver, then it can actually be easier to code in assembly language (dealing with interrupts, accessing a fixed memory location are easier in assembly than they are in C). The other case where assembly is still useful is when you are doing overflow checks, or fixed point arithmetic. For the GBA I wrote 16.16 fixed point code to replace the floating point values for my flight simulator, I used a typedef so that I could switch between the 2 implementations, I got it running using floating point but for production use I went to assembly language routines to do 16.16*16.16 number (32 bit * 32 bit >> 16), since that is normally a single assembly language instruction but in C, you have to do a full 64 bit multiply by casting one of the elements to a long long which is wasteful.

I used to have a library of macros to simplify programming in assembly, which enabled me to do ADD n,REG, and depending on the value of n would generate 0 bytes of code if n was 0, an ADDQ instruction in n was 8 or less, then I would use that macro in
a higher level macro, to unroll loops. When you are programming on an 8 Mhz processor, every cycle counts, but on modern processors most of these sort of tricks are not needed since you are waiting on memory a lot of the time, so cache considerations are more important.

I still think it is useful to know assembly though, since you can see how the code generated by the compiler will execute (it’s one of the reasons null terminated strings were used, since they can be efficiently processed in assembly language).

Apr 1, 2013 at 11:14 am

I agree that it’s absolutely useful to understand low-level languages – if for nothing more than to expand your knowledge!

Obviously there are contexts of coding that lend themselves more to low-level languages – driver development being an example. However, the majority of developers out in the field are working on business applications and have little use of this knowledge – outside of purely educational purposes , that is.