Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr performing worse at runtime

I wrote the following code to test the time taken for a constexpr factorial to evaluate vs normal way

#include<iostream>
#include<chrono>

constexpr long int factorialC(long int x){  return x*(x <2?1 : factorialC(x-1));}
using ns = std::chrono::nanoseconds;
using get_time = std::chrono::steady_clock;
void factorial(long int x){
    long int suma=1;
    for(long int i=1; i<=x;i++)
    {
        suma=suma*i;

    }
    std::cout<<suma<<std::endl;
}

int main(){
    long int x = 13;

    std::cout<<"Now calling the constexpr"<<std::endl;
    auto start1 = get_time::now();
    std::cout<<factorialC(x)<<std::endl;
    auto end1 = get_time::now();

    std::cout<<"Now calling the normal"<<std::endl;
    auto start2 = get_time::now();
    factorial(x);
    auto end2 = get_time::now();
    std::cout<<"Elapsed time for constexpr is "<<std::chrono::duration_cast<ns>(end1-start1).count()
    <<" Elapsed time for normal is "<<std::chrono::duration_cast<ns>(end2-start2).count()<<std::endl;
}

When I run the code I am getting

Now calling the constexpr                                                                                                   
1932053504                                                                                                                  
Now calling the normal                                                                                                      
1932053504                                                                                                                  
Elapsed time for constexpr is 81812 Elapsed time for normal is 72428  

But constexpr should take nearly "0" time because it has already been calculated during compilation time .

But surprisingly constexpr calculation takes more time than a normal factorial to work. I have tried to follow this question, but I am not able understand the answer in my context.

Please help me to understand it.

I compiled the code through (filename is constexpr.cpp)

g++ --std=c++11 constexpr.cpp   

V2:-

after @rici input, I did change line number 18 to

const long int x =13;

The results now are

Now calling the constexpr                                                                                                   
1932053504                                                                                                                  
Now calling the normal                                                                                                      
1932053504                                                                                                                  
Elapsed time for constexpr is 114653 Elapsed time for normal is 119052  

It seems once I mentioned the x to be const, the compiler is calculating factorialC at compile time

I am using 4.9.3 version of g++ from MinGW32 on windows

like image 431
srinath29 Avatar asked Sep 19 '16 16:09

srinath29


3 Answers

The problem is that something constexpr is not guaranteed to be evaluated at compile time. The keyword constexpr just says that it can, but the compiler is free to evaluate it at run time too, as it sees fit.

The difference in run time is probably because you 1) aren't doing it enough (one iteration is nothing) and 2) recursion isn't as fast as iteration (I think, although the difference is minimal).

To guarantee compile time evaluation, you will have to use it in a context where the compiler has to evaluate it at compile time, something like a template for example:

template<unsigned long long n>
auto evaluate() { return n; }

//...
auto start1 = get_time::now();
std::cout << evaluate<factorialC(x)>() << std::endl; //factorialC is evaluted
                                                     //at compile timme
auto end1 = get_time::now();

There is also a standard library function for evaluate, std::integral_constant. You can use that instead.

like image 76
Rakete1111 Avatar answered Nov 15 '22 03:11

Rakete1111


long int x = 13; That''s not a constant expression, so the compiler can't evaluate factorial(x); at compile time.

Try to send it constant values, like a constexpr value so it can do the evaluation:

int main(){
    long int x = 13;
    constexpr long y = 13;

    std::cout << "Now calling the constexpr" << std::endl;

    auto start1 = get_time::now();

    // Notice the use of a constexpr value here!
    std::cout << factorialC(y) << std::endl;
    auto end1 = get_time::now();


    std::cout << "Now calling the normal" << std::endl;
    auto start2 = get_time::now();

    // Simply call your function witha runtime value.
    // Try to ensure that the compiler don't inline the obvious value of x
    std::cout << factorialC(x) << std::endl;
    auto end2 = get_time::now();

    std::cout << "Elapsed time for constexpr is "
        << std::chrono::duration_cast<ns>(end1-start1).count()
        << " Elapsed time for normal is "
        << std::chrono::duration_cast<ns>(end2-start2).count()
        << std::endl;
}

By the way, you should compaire apples with apples when talking about performance.

like image 29
Guillaume Racicot Avatar answered Nov 15 '22 02:11

Guillaume Racicot


Note, that it can not be computed at a compile time, because compiler does not know nothing about a value of a long int x in this function:

 constexpr long int factorialC(long int x)

If you want compile time factorial you can use templates instead. Something like:

 #include <iostream>
 template<int N> inline int factorial(){ return N*factorial<N-1>(); }
 template<> inline int factorial<1>(){ return 1; }

 int main()
 {
     std::cout << factorial<13>() << std::endl;
     return 0;
 }
like image 1
Alex Chudinov Avatar answered Nov 15 '22 02:11

Alex Chudinov