What kind of optimization does const offer in C/C++?

C++CConstantsCompiler Optimization

C++ Problem Overview


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)?

C++ Solutions


Solution 1 - C++

Source

Case - 1:

When you declare a const in your program,

int const x = 2;

Compiler can optimize away this const by not providing storage to this variable rather add it in symbol table. So, subsequent read just need 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.


Case - 2:

> "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.


Case - 3:

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().

Solution 2 - C++

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).

Solution 3 - C++

> 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[];

Solution 4 - C++

SomeClass* const pObj creates a constant object of pointer type. There exists no safe method of changing such an object, so the compiler can, for example, cache it into a register with only one memory read, even if its address is taken.

The others don't enable any optimizations specifically, although the const qualifier on the type will affect overload resolution and possibly result in different and faster functions being selected.

Solution 5 - C++

The exact effects of const differ for each context where it is used. If const is used while declaring an variable, it is physically const and potently resides in read-only memory.

const int x = 123;

Trying to cast the const-ness away is undefined behavour:

> Even though const_cast may remove constness or volatility from any pointer or reference, using the resulting pointer or reference to write to an object that was declared const or to access an object that was declared volatile invokes undefined behavior. cppreference/const_cast

So in this case, the compiler may assume that the value of x is always 123. This opens some optimization potential (constants propagation)

For functions it's a different matter. Suppose:

void doFancyStuff(const MyObject& o);

our function doFancyStuff may do any of the following things with o.

  1. not modify the object.
  2. cast the constness away, then modify the object
  3. modify an mutable data member of MyObject

Note that if you call our function with an instance of MyObject that was declared as const, you'll invoke undefined behavior with #2.

Guru question: will the following invoke undefined behavior?

const int x = 1;
auto lam = [x]() mutable {const_cast<int&>(x) = 2;};
lam();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUnTraDeView Question on Stackoverflow
Solution 1 - C++raviView Answer on Stackoverflow
Solution 2 - C++Michael Aaron SafyanView Answer on Stackoverflow
Solution 3 - C++justinView Answer on Stackoverflow
Solution 4 - C++Ben VoigtView Answer on Stackoverflow
Solution 5 - C++StefanView Answer on Stackoverflow