Home Item 9 - constexpr (English)
Post
Cancel

Item 9 - constexpr (English)

The constexpr Keyword

C++11 introduced the constexpr keyword, which is used to declare functions or variables that can be evaluated at compile time. Functions or variables marked as constexpr can be evaluated at compile time instead of runtime, thereby improving program performance and efficiency. The typical use cases for constexpr include scenarios where values need to be calculated at compile time, such as in template metaprogramming or when declaring constant expressions.

In simple terms, constexpr shifts the time of execution from runtime to compile time.

However, not all functions should be converted to constexpr.

Here are some examples where constexpr is not suitable

  1. Situations where the value needs to be determined at runtime: If the value of a variable needs to be determined during program execution rather than at compile time, then constexpr is not suitable. For example, values obtained from user input or read from external files.

  2. Dependency on external state: If the calculation of a function or variable depends on external states that cannot be obtained at compile time, then constexpr is not appropriate. For example, accessing system time or file system status.

  3. Overly complex computation logic: If the computation logic of a function or variable is too complex to be fully unfolded and evaluated at compile time, then constexpr is not suitable. constexpr functions are evaluated at compile time, and if the computation logic is too complex, it can increase compilation time and burden the compiler.

Validation

Below, we specifically validate the part where constexpr increases compilation time.

The absolute time of calculation is not the focus, as factors like hardware can cause variation in compilation time on different computers. However, the relative time difference can indeed show the effect.

1
constexpr int result = fibonacci_constexpr(27);

Desktop View

Other 3 tests take: 3.9, 3.8, 3.4 second

1
int result = fibonacci_constexpr(27);

Desktop View

Other 3 tests take: 1.8, 1.9, 1.8 second

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <chrono>

 constexpr int fibonacci_constexpr(int n) {
     return (n <= 1) ? n : fibonacci_constexpr(n - 1) + fibonacci_constexpr(n - 2);
 }

 int fibonacci(int n) {
     return (n <= 1) ? n : fibonacci(n - 1) + fibonacci(n - 2);
 }

 int main() {


     auto start = std::chrono::high_resolution_clock::now();

     // Evaluated at compile time
     constexpr int result = fibonacci_constexpr(27);

     auto end = std::chrono::high_resolution_clock::now();

     std::chrono::duration<double> duration = end - start;

     std::cout << "Calculate at compile time: " << std::endl;
     std::cout << "time: " << duration.count() << " (s)" << std::endl;

     std::cout << "-----------------" << std::endl;

     start = std::chrono::high_resolution_clock::now();
     // Evaluated at runtime
     int result2 = fibonacci(27);

     end = std::chrono::high_resolution_clock::now();

     duration = end - start;
     std::cout << "Calculate at run time: " << std::endl;
     std::cout << "time: " << duration.count() << " (s)" << std::endl;


     return 0;
 }


Results

Desktop View

The time spent calculating at runtime is significantly longer than when fibonacci_constexpr(27) is calculated at compile time. Since fibonacci_constexpr(27) is computed during compilation, it is simply a constant value substituted at runtime, resulting in very fast execution. Conversely, fibonacci calculates the value at runtime, as evidenced by the considerably longer calculation time.

It should be clear now, huh!

☝ツ☝

This post is licensed under CC BY 4.0 by the author.

👈 ツ 👍