Computer Memory
Posted: 21 Nov 2015
Bits, bytes, and what you're actually doing with your variables.
Posted: 21 Nov 2015
Bits, bytes, and what you're actually doing with your variables.
Memory is crucial to a computer's operation, and as a programmer, it is valuable to have some understanding of how computer memory works. What are you actually doing when you declare, manipulate, or otherwise access a variable?
// C++ unsigned float myNum = 3.0; myNum += 4; std::cout << myNum << std::endl;
// JavaScript var myNum = 3.0; myNum += 4; console.log(myNum);
# Ruby my_num = 3.0 my_num += 4 puts my_num
I'll start with a little history, but by the time I come back around to these examples, you'll better understand what's going on behind the scenes.
Our current method of memory storage is inherited from a thought experiment in an essay written by Alan Turing in the late 1940's. I'll allow Turing to explain the device himself:
Logical computing machines (L.C.M.s)
In Turing (1) a certain type of discrete machine was described. It had an infinite memory capacity obtained in the form of an infinite tape marked out into squares on each of which a symbol could be printed. At any moment there is one symbol in the machine; it is called the scanned symbol. The machine can alter the scanned symbol and its behaviour is in part determined by that symbol, but the symbols on the tape elsewhere do not affect the behaviour of the machine. However the tape can be moved back and forth through the machine, this being one of the elementary operations of the machine. Any symbol on the tape may therefore eventually have an innings.
These machines will here by be called 'Logical Computing Machines'. They are chiefly of interest when we wish to consider what a machine could in principle be designed to do, when we are willing to allow it both unlimited time and unlimited storage capacity.
Turing described what would become the modern computer; the tape stores data with a 'symbol' at a prescribed index, the machine itself stores one such 'symbol', the machine behaves based on the data in storage, and the tape can be moved to allow the machine access to other data.
It wasn't long before Turing's 'Logical Computing Machine' was realized with electronics. A circuit board lined with transistors served as a practical adaptation of his thought experiment. A transistor is an electronic component with two inputs and one output. One input acts like a switch, connecting the other input with its output: when a current is passed to the switch, the internal circuit closes and current can pass from the other input to the output.
The "Collector" is where current flows in, the "Emitter" where it flows out. The "Base" is our switch: when current passes through it, the circuit closes and current can pass from "Collector" to "Emitter".
Each transistor is like one segment of Turing's tape: a blank segment corresponds to the transistor being open ( meaning no current passes ), a stamped segment to the transistor being closed ( current is allowed to pass ). This is where binary comes from: an open transistor is 0, a closed one is 1.
In computing, this is a "bit".
Two states: 0 or 1
Now we're getting back to familiar territory. A bit is a very small amount of information, the smallest possible amount really: either 1 or 0, on or off, true or false. When we string eight transistors or bits together, we get an exponentially higher amount of possible states. 2 to the power of 8 equals 256 possible states: one 'byte'.
00000000 might equal the integer '0', while 00000011 would equal '3', and 11111111 would be the maximum '255' not '256' because we include the number '0'.
Arranging clusters of transistors on a breadboard is fun to start, but once you try doing more complicated operations, it's a pain in the neck. The microchip was invented to solve this problem, so-called because the circuitry inside is miniature compared with typical electronics. Now, anyone could buy a microchip that would carry out a common, but complex, operation instead of having to build it from the ground up with every new circuitboard: a practice that lives on in the extension libraries found in various programming languages.
So people were lining up billions of transistors to store bits of information on a circuitboard: what does that have to do with us programmers? We're nearly there, we have one last step. Memory on a computer is stored in two locations, each with its own method of storage: permanent storage on a hard drive ( either a disk that is physically altered with lasers or a solid-state drive ), and temporary storage in Random Access Memory, RAM.
RAM cards still use microchips to store bits The transistor is an impermanent way to store information: once all current ceases, all the transistors switch off. This is no good for permanent memory, but Random Access Memory only needs to store information while the computer is running. RAM stores all sorts of things: visual data, the text in your word processor before you save the file to disk, and much more.
This is where our variables go.
C++, JavaScript, and Ruby all use RAM for their variables in different ways. Declaration starts the same way for all three: a Random index ( this is where the R comes from ) in the RAM is assigned the value of your variable. However, JavaScript and Ruby allow limited access to exactly how much space to set aside for your variable, while C++ allows you to define exactly how much memory to alott. Usually this isn't a problem, but with large integers, for example, Ruby has a special 'Bignum' class it will use if your integer is too big, and JavaScript will ruin your number, rounding it down to fit it in the range allowed by its 'Number' object.
// C++ unsigned float myNum = 3.0; // The number '3.0' assigned to // random memory location myNum += 4; // The value at that location // increased by four cout << myNum; // The value named by 'myNum' is // printed in the terminal
// JavaScript var myNum = 3.0; // The number '3.0' assigned to // random memory location myNum += 4; // The result of this sum converted // to an integer, '7', assigned to // a random memory location console.log( myNum ); // The value to which 'myNum' // points is printed in the console
# Ruby my_num = 3.0 # The number '3.0' assigned to # random memory location my_num += 4 # The result of this sum, '7.0', # assigned to a random memory # location puts my_num # The value to which 'myNum' points # is printed in the console
Accessing variables works much differently. C++ allows you to manipulate the stored data directly using the variable name, or create a pointer to that data. The pointer itself stores the memory index ( like an array's index, remember? ) and it can be called in a way that returns the value at that index, but it cannot alter the value in any way. Both JavaScript and Ruby use only pointers; you can't alter the value stored in the memory. When you perform an operation on a variable you actually pass the new value to an unused location in the memory and reassign the variable to point there. This allows some flexibility; in C++, a variable must be declared with its type ( integer, float, character, string, etc ) and that type cannot change, thus: 'Static Typing'. Ruby and JavaScript boast 'Dynamic Typing': a named variable can change type as you need it, since you aren't altering data in the memory, just where the pointer points.
In the early days of computing, when it was common to use relatively large transistors and much fewer of them, dynamic typing would have been unthinkable. Programmers had to squeeze every last bit out of a system, optimizing their software to run the way they wanted without comprimise. To create a new variable every time you carried out an expression would put an unfathomable load on your Commodore 64, TI-99/4A, or BBC Micro. Modern computers can handle this method of storing and retrieving data with ease. Although languages like C++ allow the programmer to be more efficient, the difference is often negligable; the amount of time the programmer saves by using dynamic typing is well worth the slight drop in performance.
When you create a variable, you switch on and off millions of microscopic transistors in your computer's RAM. In fact as long as your computer is turned on, all sorts of values are passing in and out of different parts of the RAM just to keep background processes running. As a programmer, it's important to understand how you interact with the RAM. Each coding language is likely to do it differently, even in small ways. Learn how your language uses memory and be greatful for all the hard work that has gone into electronics, computers, and programming that allows you to write a program without having to plug wires and transistors into a breadboard first.
Previous: Ruby vs JavaScript | Next: Building a Website