0

When I execute this code, the result change between two execution but I don't understand why. sometimes the results for u/v/w/x are "u 46368 <0ms, soit 0 ns>" OR "u 46368 <1ms, soit 1000100 ns> and this is random. But when a fonction is constexpr the evaluation must be do at the compile time. But here sometime is do at the compile time and sometime at the runtime.

The other question is why for the point w and x the evaluation is sometime at the compile time because the variables are not constexpr.

#include <iostream>
#include <chrono>
using namespace std;

#define CPP14 0
#define CONSTEXPR_FCT !0

#if CONSTEXPR_FCT
    constexpr unsigned fibonacci(unsigned nb)
#else
    unsigned fibonacci(unsigned nb)
#endif

{
#if !CPP14
   return ((nb == 1) ? 1 : ((nb == 0) ? 0 :
   fibonacci(nb - 2) + fibonacci(nb - 1)));
#else
   if (nb == 1)
   {
       return 1;
   }
   else if (nb == 0)
   {
       return 0;
   }
   else
   {
       return fibonacci(nb - 2) + fibonacci(nb - 1);
   }
#endif
 }

int main()
{
    cout << "a) ";
    for (unsigned u { 0 }; u < 10; ++u)
    {
        cout << fibonacci(u) << " ";
    }
    cout << endl;

constexpr unsigned depth { 24u };
// 24u : limite de profondeur recursive avec clang++ 4.0.1

cout << "\nb) u : ";
cout.flush(); // flush... sans passage a la ligne !
auto start = chrono::high_resolution_clock::now();
// inference de type : plus tard !
unsigned u { fibonacci(depth) };
auto end = chrono::high_resolution_clock::now();

cout << u << " (" <<
chrono::duration_cast<chrono::milliseconds>(end - start).count()
<< " ms, soit " <<
chrono::duration_cast<chrono::nanoseconds>(end - start).count()
<< " ns)" << endl;

// -----------------

cout << "c) v : ";
cout.flush();
start = chrono::high_resolution_clock::now();
unsigned v { fibonacci(depth) };
end = chrono::high_resolution_clock::now();
cout << v << " (" <<
chrono::duration_cast<chrono::milliseconds>(end - start).count()
<< " ms, soit " <<
chrono::duration_cast<chrono::nanoseconds>(end - start).count()
<< " ns)" << endl;

// -----------------

#if CONSTEXPR_FCT
cout << "d) cxu : ";
cout.flush();
start = chrono::high_resolution_clock::now();
constexpr unsigned cxu { fibonacci(depth) };
end = chrono::high_resolution_clock::now();
cout << cxu << " (" <<
chrono::duration_cast<chrono::nanoseconds>(end - start).count() << " ns)" << endl;
#endif

// -----------------

cout << "e) w : ";
cout.flush();
start = chrono::high_resolution_clock::now();
unsigned w { fibonacci(depth) };
end = chrono::high_resolution_clock::now();
cout << w << " (" <<
chrono::duration_cast<chrono::milliseconds>(end - start).count() << " ms, soit " <<
chrono::duration_cast<chrono::nanoseconds>(end - start).count()  << " ns)" << endl;

// -----------------

cout << "f) cu : ";
cout.flush();
start = chrono::high_resolution_clock::now();
const unsigned cu { fibonacci(depth) };
end = chrono::high_resolution_clock::now();
cout << cu << " (" << chrono::duration_cast<chrono::nanoseconds>(end-start).count() << " ns)" << endl;

// -----------------

cout << "e) x : ";
cout.flush();
start = chrono::high_resolution_clock::now();    
unsigned x { fibonacci(depth) };
end = chrono::high_resolution_clock::now();
cout << x << " (" <<
chrono::duration_cast<chrono::milliseconds>(end - start).count()  << " ms, soit " <<  chrono::duration_cast<chrono::nanoseconds>(end - start).count() << " ns)" << endl;

return 0;
}

The output is:

a) 0 1 1 2 3 5 8 13 21 34

b) u 46368 <1ms, soit 1000100ns> or u 46368 <0ms, soit 0 ns>
c) v 46368 <1ms, soit 1000100ns> or v 46368 <0ms, soit 0 ns>
d) cxu : 46368 <0 ns>`enter code here`
e) w 46368 <1ms, soit 1000100ns> or w 46368 <0ms, soit 0 ns>
f) cu : 46368 <0ns>
e) x 46368 <1ms, soit 1000100ns> or x 46368 <0ms, soit 0 ns>
Ben
  • 131
  • 1
  • 1
  • 7
  • 1
    Are you saying, that you always get either exactly `0 ns` or exactly `1000100 ns`? That seems very odd, e.g. [on godbolt with gcc](https://godbolt.org/z/uctDGm) you get random short durations. Can you specify your compiler, version and used command line options? – walnut Aug 31 '19 at 17:48
  • the value change between 0 so the evaluation is made at the compile time and sometime the valor is different of 0 in my last exec the valor is 1000100ns bu is not always the same time. Sometime more sometime less. And if the valor is not 0 the evaluation is made at the runtime. But why sometime made it at the runtime and sometime at the compiletime – Ben Aug 31 '19 at 17:55
  • 2
    Possible duplicate of [When does a constexpr function get evaluated at compile time?](https://stackoverflow.com/questions/14248235/when-does-a-constexpr-function-get-evaluated-at-compile-time) – walnut Aug 31 '19 at 17:56
  • `But when a fonction is constexpr the evaluation must be do at the compile time.`: That is not true, see linked duplicate. The only value here requiring compile-time computation is `cxu` because it is a `constexpr` variable. – walnut Aug 31 '19 at 17:57
  • But why for the points w and x this is sometime made at the compile time but the variable unsigned x and unsigned w are not constexpr or const ? – Ben Aug 31 '19 at 18:33
  • The expression `fibonacci(depth)` is a constant expression in any case, meaning that the compiler may choose to compile it at compile-time in any situation. Only the initialization of `x` and `w` (but also `cu`) from this compile-time constant would then be done at runtime. If your compiler sometimes does and sometimes doesn't do it this way, we would need to know which compiler exactly you are using to answer why it does that. – walnut Aug 31 '19 at 18:40
  • I use the compiler: MinGW 5.3.0 32 bit for C++, type: MinGW – Ben Aug 31 '19 at 19:24
  • Have you enabled optimizations? (`-O2` flag) With godbolt I get a result [similar to yours without](https://godbolt.org/z/Hv7I90) but [better with](https://godbolt.org/z/QX655a). In any case, that compiler is a few years old. The newer version optimizes all your cases, as I showed in my first comment. I suggest updating your compiler. – walnut Aug 31 '19 at 19:38
  • Thank you for your answer. No I have enable none optimisations. I use the same version used at school for have the same result. – Ben Aug 31 '19 at 21:37
  • If you do not enable optimizations, then the program will often be executing much slower than it could be. You should always enable at least `-O1` or `-Og`, but probably `-O2`, except in few specific debugging situations. Without optimizations there is no reason to expect the compiler to calculate anything at compile-time that it doesn't have to. – walnut Aug 31 '19 at 22:04

1 Answers1

2

constexpr for a function does not mean that the function will be evaluated at compile time. To some extent it is up to the compiler to decide and only when you use it in a context that requires compile time execution you can be sure about it (eg as a template parameter). What it does mean: The function can be evaluated at compile time.

From cppreference:

The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time. Such variables and functions can then be used where only compile time constant expressions are allowed (provided that appropriate function arguments are given).

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Thank you for your answer, But why for the points w and x this is sometime made at the compile time but the variable unsigned x and unsigned w are not constexpr or const ? – Ben Aug 31 '19 at 18:25