I know that where possible you should use the const keyword when passing parameters around by reference or by pointer for readability reasons. Is there any optimizations that the compiler can do if I specify that an argument is constant?
There could be a few cases:
Function parameters:
Constant reference:
void foo(const SomeClass& obj)
Constant SomeClass object:
void foo(const SomeClass* pObj)
And constant pointer to SomeClass:
void foo(SomeClass* const pObj)
Variable declarations:
const int i = 1234
Function declarations:
const char* foo()
What kind of compiler optimizations each one offers (if any)?
const correctness can't improve performance because const_cast and mutable are in the language, and allow code to conformingly break the rules. This gets even worse in C++11, where your const data may e.g. be a pointer to a std::atomic , meaning the compiler has to respect changes made by other threads.
The const keyword specifies that a variable's value is constant and tells the compiler to prevent the programmer from modifying it.
In C, C++, and D, all data types, including those defined by the user, can be declared const , and const-correctness dictates that all variables or objects should be declared as such unless they need to be modified.
Compiler optimization is generally implemented using a sequence of optimizing transformations, algorithms which take a program and transform it to produce a semantically equivalent output program that uses fewer resources or executes faster.
Source
When you declare a const
in your program,
int const x = 2;
Compiler can optimize away this const
by not providing storage for this variable; instead it can be added to the symbol table. So a subsequent read just needs indirection into the symbol table rather than instructions to fetch value from memory.
Note: If you do something like:
const int x = 1;
const int* y = &x;
Then this would force compiler to allocate space for x
. So, that degree of optimization is not possible for this case.
In terms of function parameters const
means that parameter is not modified in the function. As far as I know, there's no substantial performance gain for using const
; rather it's a means to ensure correctness.
"Does declaring the parameter and/or the return value as const help the compiler to generate more optimal code?"
const Y& f( const X& x )
{
// ... do something with x and find a Y object ...
return someY;
}
What could the compiler do better? Could it avoid a copy of the parameter or the return value?
No, as argument is already passed by reference.
Could it put a copy of x or someY into read-only memory?
No, as both x
and someY
live outside its scope and come from and/or are given to the outside world. Even if someY
is dynamically allocated on the fly within f()
itself, it and its ownership are given up to the caller.
What about possible optimizations of code that appears inside the body of f()? Because of the const, could the compiler somehow improve the code it generates for the body of f()?
Even when you call a const member function, the compiler can't assume that the bits of object x
or object someY
won't be changed. Further, there are additional problems (unless the compiler performs global optimization): The compiler also may not know for sure that no other code might have a non-const reference that aliases the same object as x
and/or someY
, and whether any such non-const references to the same object might get used incidentally during the execution of f();
and the compiler may not even know whether the real objects, to which x
and someY
are merely references, were actually declared const in the first place.
void f( const Z z )
{
// ...
}
Will there be any optimization in this?
Yes because the compiler knows that z
truly is a const object, it could perform some useful optimizations even without global analysis. For example, if the body of f()
contains a call like g( &z )
, the compiler can be sure that the non-mutable parts of z
do not change during the call to g()
.
Before giving any answer, I want to emphasize that the reason to use or not use const
really ought to be for program correctness and for clarity for other developers more so than for compiler optimizations; that is, making a parameter const
documents that the method will not modify that parameter, and making a member function const
documents that that member will not modify the object of which it is a member (at least not in a way that logically changes the output from any other const member function). Doing this, for example, allows developers to avoid making unnecessary copies of objects (because they don't have to worry that the original will be destroyed or modified) or to avoid unnecessary thread synchronization (e.g. by knowing that all threads merely read and do not mutate the object in question).
In terms of optimizations a compiler could make, at least in theory, albeit in an optimization mode that allows it to make certain non-standard assumptions that could break standard C++ code, consider:
for (int i = 0; i < obj.length(); ++i) {
f(obj);
}
Suppose the length
function is marked as const
but is actually an expensive operation (let's say it actually operates in O(n) time instead of O(1) time). If the function f
takes its parameter by const
reference, then the compiler could potentially optimize this loop to:
int cached_length = obj.length();
for (int i = 0; i < cached_length; ++i) {
f(obj);
}
... because the fact that the function f
does not modify the parameter guarantees that the length
function should return the same values each time given that the object has not changed. However, if f
is declared to take the parameter by a mutable reference, then length
would need to be recomputed on each iteration of the loop, as f
could have modified the object in a way to produce a change in the value.
As pointed out in the comments, this is assuming a number of additional caveats and would only be possible when invoking the compiler in a non-standard mode that allows it to make additional assumptions (such as that const
methods are strictly a function of their inputs and that optimizations can assume that code will never use const_cast
to convert a const reference parameter to a mutable reference).
Function parameters:
const
is not significant for referenced memory. It's like tying a hand behind the optimizer's back.
Suppose you call another function (e.g. void bar()
) in foo
which has no visible definition. The optimizer will have a restriction because it has no way of knowing whether or not bar
has modified the function parameter passed to foo
(e.g. via access to global memory). Potential to modify memory externally and aliasing introduce significant restrictions for optimizers in this area.
Although you did not ask, const
values for function parameters does allow optimizations because the optimizer is guaranteed a const
object. Of course, the cost to copy that parameter may be much higher than the optimizer's benefits.
See: http://www.gotw.ca/gotw/081.htm
Variable declarations:
const int i = 1234
This depends on where it is declared, when it is created, and the type. This category is largely where const
optimizations exist. It is undefined to modify a const
object or known constant, so the compiler is allowed to make some optimizations; it assumes you do not invoke undefined behavior and that introduces some guarantees.
const int A(10);
foo(A);
// compiler can assume A's not been modified by foo
Obviously, an optimizer can also identify variables which do not change:
for (int i(0), n(10); i < n; ++i) { // << n is not const
std::cout << i << ' ';
}
Function declarations:
const char* foo()
Not significant. The referenced memory may be modified externally. If the referenced variable returned by foo
is visible, then an optimizer could make an optimization, but that has nothing to do with the presence/absence of const
on the function's return type.
Again, a const
value or object is different:
extern const char foo[];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With