jeremy radcliff
jeremy radcliff

Reputation: 1109

How can I make this function timing macro work in C?

From a comment to this answer: https://stackoverflow.com/a/459704/2812104

I tried to make a macro function that takes a function as argument and times it using clock(). However I keep getting an error message saying that start is undeclared and I can't figure it out since I do declare it earlier in the macro

(this is all on the same line in my code in case it makes a difference; I'm breaking it up in two lines here for readability):

#define timing(a): clock_t start = clock(); a; clock_t stop = clock(); 
printf("Elapsed: %f seconds\n", (double)(stop - start) / CLOCKS_PER_SEC);

Upvotes: 2

Views: 1783

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 755064

The immediate problem is the colon after timing(a) — that wants a label to precede it, but you can't use labels on variable definitions, so you need to lose the colon:

#define timing(a) \
    clock_t start = clock(); \
    a; \
    clock_t stop = clock(); \
    printf("Elapsed: %f seconds\n", (double)(stop - start) / CLOCKS_PER_SEC);

You should probably also avoid polluting the function's name space. The classic way to do that is a do { … } while (0) loop:

#define timing(a) \
    do { \
        clock_t start = clock(); \
        a; \
        clock_t stop = clock(); \
        printf("Elapsed: %f seconds\n", (double)(stop - start) / CLOCKS_PER_SEC); \
    } while (0)

This creates a statement block that can be used legitimately even in an unprotected if block:

if (do_it_this_way)
    timing(some_func(1, 37, 91));
else
    timing(some_func(2, 41, 87));

If you use just { … } around the macro body, this generates an error (because the semicolon after 91 means the else doesn't belong to any if — the expansion is if (do_it_this_way) { … }; else …).

I'm not particularly keen on this technique. I use a logically opaque type, Clock, with support functions such as:

extern void  clk_init(Clock *clk);
extern void  clk_start(Clock *clk);
extern void  clk_stop(Clock *clk);
extern char *clk_elapsed_ms(Clock *clk, char *buffer, size_t buflen);
extern char *clk_elapsed_us(Clock *clk, char *buffer, size_t buflen);
extern char *clk_elapsed_ns(Clock *clk, char *buffer, size_t buflen);

The calling code creates and initializes a Clock, and can then start the clock before the loop, stop the clock after it, and at its leisure analyze the elapsed time. You can find this code on Github https://github.com/jleffler/soq/tree/master/src/libsoq in timer.c and timer.h. You can find examples of it being used if you search on SO with the term 'user:15168 clk_elapsed_us' (e.g. How to wrap around a range?).

Upvotes: 5

Related Questions