1

I am writing the following C code to get the time taken to perform a simple operation using getitimer and setitimer.

#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>

#define INTERVAL 1        /* number of milliseconds to go off */

int main() {
    double sum = 0;
    struct itimerval initial, updated;

    initial.it_value.tv_sec     = INTERVAL;
    initial.it_value.tv_usec    = 999999;
    initial.it_interval = initial.it_value;
    memcpy(&(initial.it_interval), &(initial.it_value), sizeof( initial.it_value )); 
    printf("%ld\n", initial.it_value.tv_usec);

    if (setitimer(ITIMER_VIRTUAL, &initial, NULL) == -1) {
        perror("error calling setitimer()");
        exit(1);
    }

    for (unsigned int i; i < 100; i++) 
        sum += 1./i;

    if (getitimer(ITIMER_REAL, &updated) == -1) {
        perror("error calling getitimer()");
        exit(1);
    }

    printf("Time started = %ld\n; Time ended = %ld\n: Time taken = %ld\n",
           initial.it_value.tv_usec, updated.it_value.tv_usec,
           initial.it_value.tv_usec - updated.it_value.tv_usec);
    return 0;
}

I have compiled with:

$ gcc -o timer -std=c99 -Wall -pedantic getitimer.c -lrt -03

However, my answer is always 999999 (I have raised and decreased the 100):

 ./timer 
999999
Time started = 999999
; Time endd = 0
: Time taken = 999999

What is my error? Also, I wanted to ask what is the highest precision I can get using a progrma like this?

Thanks very much!

user3236841
  • 1,088
  • 1
  • 15
  • 39
  • The edited code is not an improvement. The value INT_MAX is outside the range of permitted values for the `tv_usec` component (valid range is 0..999999 microseconds), hence the error. – Jonathan Leffler Feb 27 '16 at 19:06
  • thanks! However, no matter how big or small my loop, I am getting 0 at the end. – user3236841 Feb 27 '16 at 22:37

3 Answers3

1

the main thing I see is the division operations are using integer division. So:

initial.it_value.tv_sec     = INTERVAL/1000000;

places 0 in tv.sec

initial.it_value.tv_usec    = (INTERVAL/1000000) * 1000000;

places 0 in tv_usec

initial.it_interval = initial.it_value;

in general, when assigning a multi field struct, use memcpy() rather than a direct assignment. (direct assignment will work for initialization but not for assignment.)

So, the posted code sets the 'interval' to 0

so of course, the resulting values are 0

this is a key statement from the man page for setitimer() and getitimer()

"Timers decrement from it_value to zero, generate a signal, and reset to it_interval. A timer which is set to zero (it_value is zero or the timer expires and it_interval is zero) stops."

Suggest: following edited

initial.it_value.tv_set = INTERVAL;
initial.it_value.tv_usec = 0;

memcpy( &(initial.it_interval), &(initial.it_value), sizeof( initial.it_value ) );

...
user3629249
  • 16,402
  • 1
  • 16
  • 17
  • 1
    What's your reasoning behind 'use `memcpy()` rather than assignment'? It seems highly dubious to me. – Jonathan Leffler Feb 27 '16 at 09:01
  • This does not work: in fact does not even compile. I guess I did not get the suggestion! – user3236841 Feb 27 '16 at 13:43
  • @user3236841, I do see where I made a keypunch error of typing `tvuser` when I should have typed `tv_usec`. However, that keypunch error should have been obvious to you. BTW: I just compiled/ran your posted code, with my suggested modifications, It cleanly compiled. It ran and produced the expected output. – user3629249 Feb 27 '16 at 20:33
  • Sorry, I made the corrections earlier and the code did not compile. Here is what I got: getitimer.c: In function ‘main’: getitimer.c:16:10: error: incompatible type for argument 1 of ‘memcpy’ memcpy( initial.it_interval, initial.it_value, sizeof( initial.it_value )); aand so on..........could you please post your complete code? ^ In file included from getitimer.c:5:0: /usr/include/string.h:42:14: note: expected ‘void * restrict’ but argument is of type ‘struct timeval’ extern void *memcpy (void *__restrict __dest, const void *__restrict __src, ^ – user3236841 Feb 27 '16 at 21:39
  • Tthe first two parameters to the `memcpy() function must be the addresses not the contents. Suggest using: `memcpy( &(initial.it_interval), &(initial.it_value), sizeof( initial.it_value ) );` – user3629249 Feb 27 '16 at 21:42
  • thanks! the program compiles but the problem does not go away. I will submit an edited program and you can try it and see. thanks! – user3236841 Feb 27 '16 at 22:34
1

the main problem with the latest code is that the call to getitimer() is referencing a different timer than the call to setitime()

However, the following code makes it simple to use

#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
//#include <limits.h>
//#include <string.h>

// do not place comments on same line as #define statement
// always surround numeric values with parens to avoid 'text replacement' errors
// wrong comment:  number of milliseconds to go off

// number of seconds in interval
#define INTERVAL (1)

// number of usec in interval
#define MICRO_INTERVAL (999999)

int main() {
    //double sum = 0;
    struct itimerval initial;
    struct itimerval updated;

    initial.it_value.tv_sec     = INTERVAL;
    initial.it_value.tv_usec    = MICRO_INTERVAL;
    initial.it_interval.tv_sec  = INTERVAL;
    initial.it_interval.tv_usec = MICRO_INTERVAL;

    // remove this line: initial.it_interval = initial.it_value;
    // remove this line: memcpy(&(initial.it_interval), &(initial.it_value), sizeof( initial.it_value ));
    //printf("%ld\n", initial.it_value.tv_usec);

    printf( "Time value: %ld.%ld\n", initial.it_value.tv_sec, initial.it_value.tv_usec );
    printf( "Time interval: %ld.%ld\n", initial.it_interval.tv_sec, initial.it_interval.tv_usec );

    if (setitimer(ITIMER_VIRTUAL, &initial, &updated) == -1)
    {
        perror("error calling setitimer()");
        exit(1);
    }

    //for (unsigned int i=0; i < 10; i++)  // must initialize the 'i' variable
    //    sum += 1./i;

    // the 'which' parameter should be 'ITIMER_VIRTUAL'
    //     as that is what was started in the call to setitimer()
    //if (getitimer(ITIMER_REAL, &updated) == -1)
    //{
    //    perror("error calling getitimer()");
    //    exit(1);
    //}

    if (setitimer(ITIMER_VIRTUAL, &initial, &updated) == -1)
    {
        perror("error calling setitimer()");
        exit(1);
    }

    printf( "end interval counter: %ld.%ld\n", updated.it_interval.tv_sec, updated.it_interval.tv_usec );
    printf( "end value counter: %ld.%ld\n", updated.it_value.tv_sec, updated.it_value.tv_usec );

    //printf("Time started = %ld\n; Time ended = %ld\n: Time taken = %ld\n",
    //       initial.it_value.tv_usec, updated.it_value.tv_usec,
    //       initial.it_value.tv_usec - updated.it_value.tv_usec);
    return 0;
}

// accuracy is +/-1 microsecond, not millisecond

The resulting output, even with nothing being done between the two calls to setitimer() is:

Time value: 1.999999
Time interval: 1.999999
end interval counter: 1.999999
end value counter: 2.3999
user3629249
  • 16,402
  • 1
  • 16
  • 17
0

setitimer and getitimer are not the right functions to use for profiling. They relate to interval timers which are timers that generate an alarm (signal more accurately) when the timer expires.

The main options for achieving what you want are the clock or clock_gettime APIs.

kaylum
  • 13,833
  • 2
  • 22
  • 31
  • Additionally, POSIX marks both [`getitimer()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getitimer.html) and [`setitimer()`] as obsolescent, and you should aim to use the [`timer_gettime()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_gettime.html) and [`timer_settime()`] functions instead. These have nanosecond resolution (they use `struct timespec` instead of `struct timeval`). – Jonathan Leffler Feb 27 '16 at 06:01
  • Do these functions allow for virtual timing? I am interested in finding the time taken to execute the instruction for (unsigned int i; i < UINT_MAX; i++) sum += 1./i; – user3236841 Feb 27 '16 at 13:31