4

I am currently trying to implement a function that will take as input any other function and a valid set of input values for that function and return the result of the function as well as printing how long it took to execute it.

Here is what I have until now:

template<typename T, typename... Tail>

T measureAndExecute(const function<T(Tail...)> f, Tail... tail) {
    high_resolution_clock::time_point time1 = high_resolution_clock::now();
    T res = f(tail...);
    high_resolution_clock::time_point time2 = high_resolution_clock::now();
    auto duration = duration_cast<milliseconds>(time2 - time1).count();
    cout << duration << " milliseconds" << endl;
    return res;
}

And I try to run it with something like this:

int res = measureAndExecute(function<int(vector<int>&, vector<bool>&, unsigned long)> fibonacci, terms, calculated, n-1);

Which is a function to find a term in the Fibonacci series.

When I try to run it I get the following error:

error: expected '(' for function-style cast or type construction

Can somebody please give me a way forward or ideas on how to proceed?

soltzu
  • 139
  • 1
  • 10
  • 1
    Look at what you're passing as the first argument: `function&, vector&, unsigned long)> fibonacci` - this doesn't look right. If you want to construct an `std::function` from `fibonacci`, shouldn't you put `fibonacci` in parentheses, i.e. `function&, vector&, unsigned long)>(fibonacci)`? – TerraPass Aug 01 '16 at 19:46
  • I think you might find std::bind interesting. – ZivS Aug 01 '16 at 20:07
  • @TerraPass I will give it a try. – soltzu Aug 01 '16 at 20:10
  • @ZivS how would you use bind in this context? Would you bind the function with the parameters before the measureAndExecution call and then just pass the binded function to the templated function so you could save the variadic part of the template? – soltzu Aug 01 '16 at 20:11
  • I haven't tried it but I think you wouldn't need to template anything if you pass the bind object. – ZivS Aug 01 '16 at 20:14
  • Also, we use a macro for what you're trying to achieve. It is much less complicated to understand, and when you use it in the code it's readable, unlike what you'll have to write. MEASURE_AND_EXEUTE(Fibonacci(terms,...)); – ZivS Aug 01 '16 at 20:18
  • Note that `std::function` itself is much slower than creating raw lambdas and taking them as template parameters. I'm not sure how much of the slowness affects the callback versus the construction time, though, so it might not affect your benchmark much. – Kyle Strand Aug 02 '16 at 14:46

2 Answers2

4

This is a very naive way to do benchmarking. I suggest you take a look here for more advanced stuff. Nevertheless if you wanna stick to it you should change to:

template<typename F, typename... Tail>
auto measureAndExecute(F f, Tail&&... tail) -> typename std::result_of<F(Tail&&...)>::type {  
  std::chrono::high_resolution_clock::time_point time1 = std::chrono::high_resolution_clock::now();
  auto res = f(std::forward<Tail>(tail)...);
  std::chrono::high_resolution_clock::time_point time2 = std::chrono::high_resolution_clock::now();
  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(time2 - time1).count();
  std::cout << duration << " milliseconds" << std::endl;
  return res;
}
Community
  • 1
  • 1
101010
  • 41,839
  • 11
  • 94
  • 168
  • Also, there's no reason to use trailing return type. Moreover, it won't work with functions having `void` return type. – skypjack Aug 01 '16 at 20:25
  • @101010 I had a look at the link you provided, it is not that different... They use a functor instead of a method but the rest is similar, to my unexperienced eyes at least. Also, didn't now about the auto return type, would have saved me a bit of trouble trying to find the return type of the duration parameter. I was also going to say that, in their code, you can't have a return from the measure method but I guess that can be fixed with a relatively simple modification. – soltzu Aug 02 '16 at 05:30
  • @soltzu Take a look at the github link there's a benchmark tool there. Your approach misses several procedures for robust benchmarking e.g., cpu heat up, average execution time etc. – 101010 Aug 02 '16 at 08:01
2

I agree with @101010 that this is a unusual way of benchmarking a software.

That said, here is a solution that works also with functions havingvoid return type (the example in the question wouldn't have worked with them):

#include<type_traits>
#include<iostream>

struct Check {
    Check(): time1{std::chrono::high_resolution_clock::now()} {}

    ~Check() {
        std::chrono::high_resolution_clock::time_point time2 = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(time2 - time1).count();
        std::cout << duration << " milliseconds" << std::endl;
    }

    std::chrono::high_resolution_clock::time_point time1;
};


template<typename F, typename... Tail>
typename std::result_of<F(Tail&&...)>::type measureAndExecute(F f, Tail&&... tail) {  
    Check check;
    (void)check;
    return f(std::forward<Tail>(tail)...);
}

int f(int i) { return i; }
void g() { }

int main() {
    measureAndExecute(f, 42);
    measureAndExecute(g);
}

The basic idea is to create an instance of Check and exploit its lifetime to measure the time.

EDIT

As mentioned in the comments, a refinement of measureAndExecute would be:

template<typename F, typename... Tail>
typename std::result_of<F&&(Tail&&...)>::type measureAndExecute(F &&f, Tail&&... tail) {  
    Check check;
    (void)check;
    return std::forward<F>(f)(std::forward<Tail>(tail)...);
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • thanks! I'll give it a try. Nice use of the destructor. Can I ask why are you casting check to void in the second line of the measureAndExecute body? "(void)check;"? Finally, why is this way unusual compared with the one in the link provided by 101010? – soltzu Aug 02 '16 at 05:34
  • `(void)check` suppresses warnings for unused variables. That's all. :-) – skypjack Aug 02 '16 at 05:45
  • Nice trick, I used to suppress those via compiler flag. Not anymore. – soltzu Aug 02 '16 at 05:48
  • Isn't it better to pass the function by const ref?`measureAndExecute(const F& f, Tail&&... tail) ` – ZivS Aug 02 '16 at 11:41
  • @ZivS Actually, the best would be using a forwarding reference... I kept the OP code anyway. – skypjack Aug 02 '16 at 14:03
  • 1
    @ZivS Well, added an edit section to integrate the answer. Thanks for the suggestion. – skypjack Aug 02 '16 at 20:30
  • @skypjack, perhaps you can contribute here now: http://stackoverflow.com/questions/38723515/c11-stdforward-a-pointer – ZivS Aug 02 '16 at 20:36