Understanding Computer Memory and CPU
To be able to effectively program with low-level programming languages such as assembly, it is important to understand how computers structure memory, and how data is processed by the CPU. Most modern computers work off what is referred to as Von Neumann architecture. In this type of architecture, the computer is divided up into two main components, memory, and the CPU. Data is read out of the memory, processed by the CPU, and stored back in memory.
We can think of computer memory like mailboxes. Each mailbox is associated with an address, and we can place data within each mailbox to be stored until it is required. When it is required, we access the address and take the data. We can then put it back inside either the same or different mailbox. The CPU is the component responsible for accessing, manipulating, and moving data. It can fetch instructions from memory one at a time and execute them. This is often referred to as a fetch-execute cycle.
Diving further into the CPU, there are a number of components that help the CPU access, manipulate, and move data. The CPU has a program counter, which tells the computer where to fetch the next instruction from. This instruction is then passed on to the instruction decoder, which figures out what the instruction actually means. This allows the CPU to know exactly which operation needs to be performed. The CPU will then use a data bus to fetch any memory location that is used for the instruction it decoded. The data bus is an actual physical wire that connects the CPU and memory.
The CPU also has some special memory located within the processor itself. These are special types of memory referred to as registers. They are considered high speed since they are close to the actual CPU, when compared to normal memory.
When the CPU needs to process data using arithmetic or logic, it passes control to the ALU or arithmetic and logic unit. This is the location where the actual instruction is executed. Once it is executed, it is returned to either memory, or the register until it is needed again.
There are a few other interesting aspects of the CPU that we can discuss. One important component of the CPU is a hardware cache, which is a type of memory that sits between the processor and the system memory. The goal of the cache is to reduce the average cost of accessing data from memory. The registers in the processor are limited, meaning that without a cache, most data needs to be read from system memory, which takes a large amount of time. If we know data will be used later, and don't have space in registers, we can store it in the cache, which keeps it close to the CPU, for when it needs to be fetched again.
In addition to storage, a lot of processing in CPUs is done utilizing parallelization and other forms of optimization. One interesting optimization used by the CPU is known as out-of-order execution. Suppose you wrote the following Java code.
When you compile this code, it is written into assembly, so that your processor is able to easily decode and execute your code. With the code above, you might assume that the processor will store x = 3 in memory, then add 2 to x, then set y = 3. In reality, your processor is typically designed to execute any instructions without dependencies first, to minimize execution time. This being the case, the code is rearranged as follows.
This allows the processor to set the two values without dependencies, and then execute the instruction with the dependency. This will overall speed up the execution time of the program, and minimize the fetch run cycles of the CPU.
As you explore lower-level languages such as assembly, and even languages like C, you will be exposed to memory management and processor optimization more. The more of an understanding you have of these topics, you more efficient code you will be able to write. This is the main reason why low-level languages and assembly are important to learn. Even if you program in high-level languages, you can still apply the understanding of low-level processing to make sure your code is written optimally for the processor.