# Benefits of explaining low-level architecture in a programming class

When teaching an intermediate or advanced highschool programming class that also touches on general CS concepts (algorithms, etc), what are the benefits of explaining low-level computer architecture? How much added understanding of programming/computer science does a student get when learning about logic gates, for example?

My question isn't a duplicate of Is it more effective to teach low level languages before high level ones or vice versa? as it's asking about logic gates, registers, instruction set architectures, etc, and the benefits of teaching those in a programming class, not about low level programming languages in particular.

• Jun 18 '17 at 15:02

TL;DR Understanding the levels of abstraction in a computer

My AP Computer Science Principles course teaches three languages: Scratch, C, and Python. One of the 7 Big Ideas for the course is abstraction. At the end of the course, I emphasize how Scratch exists at the highest level of abstraction relative to the other two as a block-based language and how the Python interpreter we use is written in C. Connecting this to lower levels of a computer, we form a chain that looks like this (from high to low):

Scratch
Python
C
Assembly Language
Machine Code
Logic Gates


Obviously for the purposes of the class, it is a bit of a simplification as we don't cover instruction sets or adders and ALUs or assembly language specifically. However, as an introduction, students can begin to form a conception as to how a computer operates at each level. This lays the foundation for further study as students look ahead to pursuing computer science in college.

Despite any potential future benefit, I take this more philosophical approach to this topic: there is an intrinsic benefit and reward to looking "under the hood" at how things actually work; in that sense learning lower levels of computer architecture is an end in and of itself.

To contextualize my class as a whole, I use the following quote at the top of my syllabus:

"Any sufficiently advanced technology is indistinguishable from magic." -Arthur C. Clarke

Learning how charges of electricity on transistors can be constructed to form logic gates, which in turn form the building blocks for computer architecture -- that is magical.

• I love that quote. Well played. Jun 18 '17 at 17:39

Students should understand the importance of the memory hierarchy, including how caches use temporal and spatial locality to tremendously speed up memory accesses. Otherwise, a student would not know there is any difference between:

for (int i = 0; i < 1000000; i++) {
for (int j = 0; j < 1000000; j++) {
arr[i] += j;
}
}


and the logically equivalent reordered code:

for (int j = 0; j < 1000000; j++) {
for (int i = 0; i < 1000000; i++) {
arr[i] += j;
}
}


The difference is that, in the first piece of code, each array element is retrieved from memory once and placed in either a register or the cache, from which it is operated on a million times. Ignoring spatial locality (which would apply equally to both versions), the total number of reads from memory would be 1,000,000, and the total number of writes would be 1,000,000.

With the second version of code, each array element is fetched from memory once for each value of j. The total number of reads from memory would be 1,000,000 x 1,000,000 (10^12); the total number of writes would be the same.

Per Latency Numbers Every Programmer Should Know, a main memory access takes about 100 ns; an L1-cache reference takes .5 ns. Accessing a register adds no time to instruction execution.

Ignoring the times for the writes (which may not be in the critical path) and spatial locality (which applies the same to both versions) and assuming that a[i] is stored in a register, the data memory access time for the first is 10^6 * 100 ns = .1 s. The data memory access time for the second is 10^12 * 100 ns = 10^5 s.

• Okay, I'll bite. Why? With only two values, wouldn't they both be stored in registers? Or would i be in the accumulator directly,and thereby somehow get us to the address faster?
– Ben I.
Jun 20 '17 at 10:16
• @BenI. I'll add an explanation. Jun 20 '17 at 17:17
• Oh, duh! Slaps forehead. Thank you, that is a beautiful explanation. I feel dumb right now for not seeing that :P
– Ben I.
Jun 20 '17 at 17:32
• As I tell my students, for every person asking a question, there are several more wondering the same thing, so thanks for encouraging me to be more explicit. Jun 20 '17 at 17:58

A touch of some minimal explanation of logic gates, binary numbers, state machines and the processor-memory divide helps make computing seem less like magic, and more like technology (something of which they could become designers in the future).

One doesn't need to teach actually using assembly language to explain how a basic fetch execute cycle works. Etc.

Describing the low level architecture at the level of what the machine is able to do can greatly help some students to identify with the task of coding. If you do this, I think you also need to consider how to describe the link between this internal machine architecture, and the external (analogue) world. I don't think it's necessary to go into great detail, just describe the basic load/store/add as a very simple process which is powerful because it can be performed extremely rapidly.

I'm dubious about the value of going down to the level of logic gates or transistors. These are not the elements that processors are designed with (not for maybe 20 years), and seem like a little bit too much detail to present until you are at a point where manipulating and minimising logical equations makes sense. A simple assembly code expresses the right sort of low level unit, and there are plenty of examples of mechanical programmable machines that you can draw on (via you-tube) to demonstrate that the magic is realisable.

Regarding architecture specifically, I don't see a tremendous benefit in high-school level programming classes. However, at just one level or so of higher abstraction, discussing the principles of memory management as it applies to programs they write (such as stack and heap operation, how function calls are handled, etc) can make a big difference.

It is very hard to describe why tail recursion is important, or the operational output of (mystring == mystring2) in Java, or even what the code below accomplishes without discussing some lower level operation of the computer:

public static void main(String[] args) {
int[] v = new int[1];
v[0] = 5;
mystery(v);
System.out.println(v[0]);
}

public static void mystery(int[] a){
a[0] = 7;
a = new int[10];
a[0] = 10;
}