Syntax question, timing function idea

bobpellegrino

Well-known member
I had a (very incomplete) idea for a timing function. The goal would be to reduce some repetitive code when performance tuning.

Usually the profiling phase of my development includes many instances of:
Code:
    elapsedMillis t1;
    somefunc();
    printf("somefunc took %d millis\n", t1);

To streamline this, I defined timeit():
Code:
void timeit(void (*function)()) {
    elapsedMillis time;
    (*function)();
    printf("took %d\n", time);
}

So timeit(somefunc); actually works as expected. But now I'm stuck on two things:
  1. How do I do this if the timed function has arguments? timeit(somefunc(x)); does not work.
  2. How could I reference the name of the function as a string so that the timeit() function could print "somefunc() took x milliseconds"

Maybe it would be better to make it a macro instead.
 
I tried it as a macro:

Code:
#define Timeit(function_call)  \
do { \
    elapsedMillis t1; \
    function_call; \
    Serial.printf(#function_call "=%d ms\n", t1); \
} while (0)

But for some reason it always printed the same value no matter which function I called it with. I guess elapsedMillis wasn't getting re-evaluated?

The following works though:
Code:
#define Timeit(function_call)  \
do { \
    t1=micros(); \
    function_call; \
    t2=micros();    \
    t3 = t2 - t1;   \
    Serial.printf(#function_call "=%d usec\n", t3); \
} while (0)

So now I can call Timeit(myfunc(args)); and just undef Timeit whenever I'm not using it. Why doesn't elapsedMillis work though?
 
So now I can call Timeit(myfunc(args)); and just undef Timeit whenever I'm not using it. Why doesn't elapsedMillis work though?
I think it's because elapsedMillis is a C++ object, and you're passing it to printf() as if it's an integer. I'm not much of C++ programmer, but I'm looking at the source in the T4 core, and it might work if you use the syntax t1() rather than just t1. You probably want more resolution than millis(), right, so you could try it with elapsedMicros. In your version that works, you should define your time variables (t1,t2,t3) as uint32_t, because they are int by default, and you need the unsigned difference to get the right answer when micros() rolls over between t1 and t2. If you use uint32_t, then also use %lu rather than %d in the printf().
 
So timeit(somefunc); actually works as expected. But now I'm stuck on two things:
  1. How do I do this if the timed function has arguments? timeit(somefunc(x)); does not work.

You can also use std::function and std::bind to achieve this without macros:

C++:
#include <functional>
void timeit(std::function<void()> f)
{ 
   elapsedMillis time;
    f();
    printf("took %d\n", (unsigned)time);
}

void g1(){ 
  delay(120);
}

void g2(int x){
  delay(x);
}

void setup()
{
  while (!Serial);

  timeit(g1);
  timeit(std::bind(g2, 25));
}

void loop(){
}

which prints:
Code:
took 120
took 25
 
... or, if you don't like std::function / std::bind you can simply use a lambda:

C++:
void timeit(void(*f)())
{ 
   elapsedMillis time;
    f();
    printf("took %d\n", (unsigned)time);
}

void g1(){   
  delay(120);
}

void g2(int x){
  delay(x);
}

void setup()
{
  while (!Serial);

  timeit(g1); 
  timeit([]{g2(25);});
}

void loop(){
}
 
Back
Top