- Compile-time constants, like the number "6", or an "enum {x=6};", or a "const int x=6;".
The compiler loves these, and can use them to unroll loops, predo "if"
statements, transform away array indexing, etc. It's great!
// factorial, with constants. Compiler unrolls the loop. Takes 3ns.
enum {n=8};
int fact=1;
for (int i=1;i<=n;i++) fact*=i;
return fact;
(Try this in NetRun now!)
- Function arguments (by value) or local variables, like "int
x=6;". The compiler's usually smart enough to figure out when
they're really constants. Locals typically get stored in
registers, or transformed away entirely, and only rarely have to live
out in memory.
// factorial, with function parameters. Loop has to run now. Takes 11ns.
int factorial(int n) {
int fact=1;
for (int i=1;i<=n;i++) fact*=i;
return fact;
}
int foo(void) {
return factorial(8);
}
(Try this in NetRun now!)
- Caveat: "inline" functions don't cost anything. In this
case, adding "inline" means the compiler can see that n is actually a
known-value constant, and so unroll the loop. If you've got a bunch of little functions, this can be a HUGE win!
// factorial, with inline function. Compiler can unroll again! Takes 3ns.
inline int factorial(int n) {
int fact=1;
for (int i=1;i<=n;i++) fact*=i;
return fact;
}
int foo(void) {
return factorial(8);
}
(Try this in NetRun now!)
- Function parameters passed by reference, values accessed via a
pointer, or global variables. These all have to live most of
their lives in memory, since compilers are extremely paranoid about
copying these values to registers, in case somebody else changes the
in-memory version. You can help the compiler out by making sure
it knows nobody else
can change these: mark them as "const", or don't write to any other
pointers (that could be aliased to the value) or call any other
functions (that could change the value).
// factorial, with by-reference n and a global access. Two memory accesses per iteration. Takes 16ns.
int fact=0;
int factorial(int &n) {
fact=1;
for (int i=1;i<=n;i++) fact*=i;
return fact;
}
int foo(void) {
int n=8;
return factorial(n);
}
(Try this in NetRun now!)
- Variables marked "volatile". This keyword is there for
weird things like threads or I/O signal handlers that will magically
reset the in-memory copy, and the compiler will honor your demands and
always reload the value from memory.
// factorial, with volatile loop index. Four memory accesses per iteration. Takes 26ns.
int fact=0;
int factorial(int &n) {
fact=1;
for (volatile int i=1;i<=n;i++) fact*=i;
return fact;
}
int foo(void) {
int n=8;
return factorial(n);
}
(Try this in NetRun now!)