Friday 12th February 2010
by evanHere is the most useful information I know, condensed and explained.
A computer is…
- Processors – to move and transform information
- Memory – to store information to be moved or transformed
- Devices – to pass information in and out (Display, Keyboard, Network Cards, Storage Devices)
How does a computer store information?
- Memory is a huge line of “bits”
- Each bit is a tiny device that can hold a high or low electric charge.
- A bit can represent two possible numbers: zero (low charge) or one (high charge).
- Two bits can represent four possible numbers (00, 01, 10, 11), three bits have eight possible values, and so on… for N bits there are 2^N possible values.
- A number value can be represented using any “base”. Base-2, or binary is useful because each digit corresponds to a bit.
- Base-16, or hexadecimal, is also common because each digit corresponds to four bits (four bits has sixteen possible values).
- A computer may interpret numbers as characters (see ASCII), display color values, position coordinates, or anything…
- For signed numbers a bit is reserved for the sign value. For non-integer numbers, bits are divided into sign, base, and exponent (called “Floating-Point”).
- Modern computers work in groups of 32 or 64 bits, meaning that each variable has a range of 2^32 or 2^64 possible values.
- Each “byte” or 8-bit chunk of memory has a number identifier or “address”.
How does the processor process?
- A processor reads a list of instructions (or program) from memory and performs them in order.
- Most instructions can only operate on a specific kind of memory called the “registers”.
- There are typically sixteen-ish registers, where each is 32 or 64 bits.
- An instruction is represented as:
- A number, or “op code”, that corresponds to an operation
- A sequence of numbers which correspond to registers, memory addresses, or whatever – their meaning changes by operation.
- Instructions are often represented as “Assembly Language”. Some typical assembly might look like:
MOV AX, BX
Move – Copy the value stored in register BX to register AX.
ADD AX, BX
Add – Add the value stored in register BX to the value stored in register AX, and store the result in register AX.
LOAD AX, DX
Load – Copy a value from memory into register AX, where the memory address is stored in registered DX.
JMP DX
Jump – Rather than continue executing instructions normally, execute the instructions starting at the memory address in register DX.
JE AX, BX, DX
Jump If Equal – If the values in registers AX and BX are equal, “Jump” (see above) to the memory address in register DX.
- Each processor is continuously running a program called the operating system (like Windows or Unix).
- The operating system loads other programs into memory (from hard drive) and instructs processors to “jump” between them.
How are programs written?
- Programmers rarely write raw instructions. Rather, they use a “high level” language (like C++ or Java)
- The text files that contain high level language are called “source code” or source.
- A program called a “compiler” (like gcc) translates source files into raw instructions (called “binary”).
- A program called an “environment” (like Visual Studio, Eclipse) is used to edit source files, feed them to the compiler, and report errors.
- An environment usually has a “debugger” which allows the programmer to pause the program at any time and inspect or change memory.
- A source file might contain this:
int max(int a, int b)
{
if(a > b)
return a;
else
return b;
}
- this defines a “function” named max, which might be “called” somewhere else with this line:
x = max(x, 10);
- Functions operate by using a “stack” – a sequence of memory reserved for storing values in a certain order.
- In the line above, x is a “variable” – a name for a chunk of memory where data is stored.
- Let’s say the “type” of x is “int”, so the compiler will read up to 32 bits and interpret them as an integer value.
- “x = something” means to determine the value of “something” and copy it to x.
- The function max is resolved by:
- Copying inputs to the stack (in this case the value of x is the first, and 10 is the second)
- …as well as the address of the current instruction
- and then jumping to the first instruction of the sequence named “max”
- Here is what the instructions of max do:
- If the value of the first input on the stack (named a) is less than that of the second (named b), then “return” the value of a, otherwise the value of b.
- When a function “returns” a value, that value is copied to the stack, and the processor jumps back to the instruction that called the function (also remembered on the stack).
- Back at the calling line, the “x = …” says to copy the output of max from the stack to the memory location named “x”.
What are pointers and references?
- Whereas most variables are names that correspond to a memory location where a value is stored…
- Pointers and references are names that correspond to a memory location that stores the address of _another_ memory location.
- So a pointer is a variable that stores the location of another variable, or “points” to it.
- Avoid pointers unless you need them (they can be tricky). You’ll find uses for them as you go.
What are structs? classes? objects? templates? inheritance?
- These are just ways of grouping variables and functions. You’ll figure ‘em out. Use google.
What tools do I need to learn?
- An environment with a debugger. Visual Studio and Eclipse are good.
- Source control. I recommend SVN, Perforce, or Mercurial.
- A good reference manual for whatever technologies you use.
How to make programs go fast?
- Not all memory is equal. Most instructions can only operate on registers (see above, How does the processor process?)
- Register memory is very fast, but very small. Values must be swapped in and out as needed from other memory.
- Cache memory is much bigger, but also slower. When it fills up, it must also be swapped out (google “cache miss”).
- Main memory is even bigger, and even slower. But it can also fill up, which leads to swapping with the…
- Hard drive storage is the biggest and slowest of all.
- A processor spends the majority of its time waiting for memory or storage devices to read or write data.
- The trick is to compact the most frequently needed data so it can be loaded into the fastest memory and swapped out as rarely as possible.
- Another trick is to divide up the work for multiple processors to perform in parallel without too much waiting on each other.
- Some processors have instructions for special purposes (like 3D graphics) which they can perform much faster than a sequence of general purpose instructions.
- Use a system timer, or a timing program to record how long each section of a program takes to execute, and target the slowest parts or “bottlenecks”.
How to make programs “clean”?
- Ask yourself, “Will a stranger understand what I’ve written?”
- Have a plan before you start! If you’re just experimenting, be ready to throw it away and start over. Experiments make for terrible code.
- Write comments that explain your intention, but don’t describe exactly what you’re doing (the code should do that).
- Make variable and function names as self explanatory as possible (longer names are okay).
- Break the work into small tasks and use separate functions, files, and other grouping systems to separate instructions and variables by their purpose (this is called “cohesion”)..
- Avoid having one “group” of code reference another unless there is a good reason (this is called “loose coupling”)
- Try to write less whenever possible, don’t duplicate. Re-use code. (this is called “modularity”)
- Be consistent. (this is called “code convention”)
- Clean up and rearrange code when it gets too messy (this is called “refactoring”)
- Have another programmer critique your code (this is called “peer review”).
- Test! Never assume something works. Use “breakpoints” (in your debugger) to be sure that every block of code is performed correctly.
How to fix bugs?
- This will be most of the job.
- If a bug halts the system, use your debugger to examine the current function and variables. Probably some data is wrong.
- If the bug is a glitch, some data must be wrong. Try to write code that detects the bad data and halts (google “code assertion”).
- The bad data must have been written from somewhere.
- If it is an input to the halted function, check the calling function (look for “callstack” in your debugger).
- Find the last line of code which may have changed the data which looks suspicious, place a breakpoint, and try to reproduce the error.
- Another method is to add a “data breakpoint” to the address of the data that is being corrupted. The breakpoint will pause the program for inspection whenever that data is modified.
Further Reading?
- C++ FAQ Lite (If you go with C++)
- Code Complete