Does const-correctness give the compiler more room for optimization?
I know that it improves readability and makes the program less error-prone, but how much does it improve the performance?
And on a side note, what's the major difference between a reference and a const
pointer? I would assume they're stored in the memory differently, but how so?
Solution 1:
[Edit: OK so this question is more subtle than I thought at first.]
Declaring a pointer-to-const or reference-of-const never helps any compiler to optimize anything. (Although see the Update at the bottom of this answer.)
The const
declaration only indicates how an identifier will be used within the scope of its declaration; it does not say that the underlying object can not change.
Example:
int foo(const int *p) {
int x = *p;
bar(x);
x = *p;
return x;
}
The compiler cannot assume that *p
is not modified by the call to bar()
, because p
could be (e.g.) a pointer to a global int and bar()
might modify it.
If the compiler knows enough about the caller of foo()
and the contents of bar()
that it can prove bar()
does not modify *p
, then it can also perform that proof without the const declaration.
But this is true in general. Because const
only has an effect within the scope of the declaration, the compiler can already see how you are treating the pointer or reference within that scope; it already knows that you are not modifying the underlying object.
So in short, all const
does in this context is prevent you from making mistakes. It does not tell the compiler anything it does not already know, and therefore it is irrelevant for optimization.
What about functions that call foo()
? Like:
int x = 37;
foo(&x);
printf("%d\n", x);
Can the compiler prove that this prints 37, since foo()
takes a const int *
?
No. Even though foo()
takes a pointer-to-const, it might cast the const-ness away and modify the int. (This is not undefined behavior.) Here again, the compiler cannot make any assumptions in general; and if it knows enough about foo()
to make such an optimization, it will know that even without the const
.
The only time const
might allow optimizations is cases like this:
const int x = 37;
foo(&x);
printf("%d\n", x);
Here, to modify x
through any mechanism whatsoever (e.g., by taking a pointer to it and casting away the const
) is to invoke Undefined Behavior. So the compiler is free to assume you do not do that, and it can propagate the constant 37 into the printf(). This sort of optimization is legal for any object you declare const
. (In practice, a local variable to which you never take a reference will not benefit, because the compiler can already see whether you modify it within its scope.)
To answer your "side note" question, (a) a const pointer is a pointer; and (b) a const pointer can equal NULL. You are correct that the internal representation (i.e. an address) is most likely the same.
[update]
As Christoph points out in the comments, my answer is incomplete because it does not mention restrict
.
Section 6.7.3.1 (4) of the C99 standard says:
During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: T shall not be const-qualified. ...
(Here B is a basic block over which P, a restrict-pointer-to-T, is in scope.)
So if a C function foo()
is declared like this:
foo(const int * restrict p)
...then the compiler may assume that no modifications to *p
occur during the lifetime of p
-- i.e., during the execution of foo()
-- because otherwise the Behavior would be Undefined.
So in principle, combining restrict
with a pointer-to-const could enable both of the optimizations that are dismissed above. Do any compilers actually implement such an optimization, I wonder? (GCC 4.5.2, at least, does not.)
Note that restrict
only exists in C, not C++ (not even C++0x), except as a compiler-specific extension.
Solution 2:
Off the top of my head, I can think of two cases where proper const
-qualification allows additional optimizations (in cases where whole-program analysis is unavailable):
const int foo = 42;
bar(&foo);
printf("%i", foo);
Here, the compiler knows to print 42
without having to examine the body of bar()
(which might not be visible in the curent translation unit) because all modifications to foo
are illegal (this is the same as Nemo's example).
However, this is also possible without marking foo
as const
by declaring bar()
as
extern void bar(const int *restrict p);
In many cases, the programmer actually wants restrict
-qualified pointers-to-const
and not plain pointers-to-const
as function parameters, as only the former make any guarantees about the mutability of the pointed-to objects.
As to the second part of your question: For all practical purposes, a C++ reference can be thought of as a constant pointer (not a pointer to a constant value!) with automatic indirection - it is not any 'safer' or 'faster' than a pointer, just more convenient.
Solution 3:
There are two issues with const
in C++ (as far as optimization is concerned):
const_cast
mutable
const_cast
mean that even though you pass an object by const reference or const pointer, the function might cast the const-ness away and modify the object (allowed if the object is not const to begin with).
mutable
mean that even though an object is const
, some of its parts may be modified (caching behavior). Also, objects pointed to (instead of being owned) can be modified in const
methods, even when they logically are part of the object state. And finally global variables can be modified too...
const
is here to help the developer catch logical mistakes early.
Solution 4:
const-correctness generally doesn't help performance; most compilers don't even bother to track constness beyond the frontend. Marking variables as const can help, depending on the situation.
References and pointers are stored exactly the same way in memory.
Solution 5:
This really depends on the compiler/platform (it may help optimisation on some compiler that has not yet been written, or on some platform that you never use). The C and C++ standards say nothing about performance other than giving complexity requirements for some functions.
Pointers and references to const usually do not help optimisation, as the const qualification can legally be cast away in some situations, and it is possible that the object can be modified by a different non-const reference. On the other hand, declaring an object to be const can be helpful, as it guarantees that the object cannot be modified (even when passed to functions that the compiler does not know the definition of). This allows the compiler to store the const object in read-only memory, or cache its value in a centralised place, reducing the need for copies and checks for modifications.
Pointers and references are usually implemented in the exact same way, but again this is totally platform dependant. If you are really interested then you should look at the generated machine code for your platform and compiler in your program (if indeed you are using a compiler that generates machine code).