5

I'm using a open-source library for i2c bus actions. This library frequently uses a function to obtain an actual time-stamp with millisecond resolution.

Example Call:

nowtime = timer_nowtime();
while ((i2c_CheckBit(dev) == true) && ((timer_nowtime() - nowtime) < I2C_TIMEOUT));

The application using this i2c library uses a lot of CPU capacity. I figured out, that the running program the most time is calling the function timer_nowtime().

The original function:

unsigned long timer_nowtime(void) {        
    static bool usetimer = false;
    static unsigned long long inittime;
    struct tms cputime;

    if (usetimer == false)
    {
        inittime  = (unsigned long long)times(&cputime);
        usetimer = true;
    }

    return (unsigned long)((times(&cputime) - inittime)*1000UL/sysconf(_SC_CLK_TCK));
}

My aim now is, to improve the efficiency of this function. I tried it this way:

struct timespec systemtime;

clock_gettime(CLOCK_REALTIME, &systemtime);
//convert the to milliseconds timestamp
// incorrect way, because (1 / 1000000UL) always returns 0 -> thanks Pace
//return (unsigned long) ( (systemtime.tv_sec * 1000UL) + (systemtime.tv_nsec
//              * (1 / 1000000UL)));
return (unsigned long) ((systemtime.tv_sec * 1000UL)
            + (systemtime.tv_nsec / 1000000UL));

Unfortunately, I can't declare this function inline (no clue why).

Which way is more efficient to obtain an actual timestamp with ms resolution? I'm sure there is a more per-formant way to do so. Any suggestions?

thanks.

Maus
  • 2,724
  • 5
  • 32
  • 37

5 Answers5

3

Its clear that your example call uses most CPU time in timer_nowtime() function. You are polling, and the loop eats your CPU time. You could exchange the timer function with a better alternative and so you may achieve more loop iterations, but it will still use most CPU time in that function! You will not achieve using less CPU time by changing your timer function!

You may change your loop and introduce wait times - but only if it makes sense in your application, e.g.:

start = timer_nowtime();
while( i2c_CheckBit(dev) ) {
    now = timer_nowtime();
    diff = now - start;
    if( diff < I2C_TIMEOUT ) break;
    else if( diff > SOME_TRESHOLD ) usleep( 1000*std::max(1,I2C_TIMEOUT-diff-SOME_SMALL_NUMBER_1_TO_10_MAYBE) );
}

The timer: I think gettimeofday() would be a good decision, it has high precision and is available in most (all?) Unices.

Frunsi
  • 7,099
  • 5
  • 36
  • 42
  • 1
    `gettimeofday()` is not monotonic, and it's subject to NTP corrections, etc... It's not the proper solution here. You want to use `clock_gettime(CLOCK_MONOTONIC)`. – Fake Name Jan 28 '14 at 01:13
3

Note that your two examples are not equivalent; times(2) measures CPU time consumptions, whereas clock_gettime(CLOCK_REALTIME, ...) measures the wallclock time.

That being said, wallclock timers such as clock_gettime or the older gettimeofday are usually much faster than CPU timers such as times() or getrusage().

And, as already noted, your timing function is using up much CPU time because you do little else than polling it. If that is a problem, wait a little bit e.g. by calling nanosleep() or clock_nanosleep().

I think your best option is to use clock_gettime(), however with CLOCK_MONOTONIC instead of CLOCK_REALTIME.

janneb
  • 36,249
  • 2
  • 81
  • 97
  • 2
    As these times are being used for bitbanging a hardware bus, wallclock time makes more sense than CPU time. So I agree with your CLOCK_MONOTONIC suggestion. – caf Jan 05 '10 at 03:52
1

If your program only executes on recent Intel/AMD processors, but not too recent (clock throttling is not handled very well), the RDTSC assembly instruction is the best way to get a timestamp. The resolution is close to the actual clock of the processor and it is independent from the system (this means it is biased by interrupts, too. You can't have your cake and eat it too).

This page has an example in C.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • 1
    rdtsc can also be problematic on multiprocessors, if your thread happens to switch to another core/hwthread, then the counter value will be nonsense, as they are not synchronized among cores. Hence it's almost always better to use the OS provided API's, in the case of POSIX clock_gettime(). – janneb Jan 04 '10 at 14:21
  • RDTSC should not be used for timing purposes (not anymore..): http://en.wikipedia.org/wiki/Time_Stamp_Counter! – Frunsi Jan 04 '10 at 14:32
  • I did say "not too recent" :) – Pascal Cuoq Jan 04 '10 at 14:51
  • Since the timerfunction is used to determine how much time passed by and the software is running on a single CPU embedded device, this solution would be an option. Of course this wouldn't be POSIX conform and hard to port onto another platform. – Maus Jan 06 '10 at 11:52
  • @Frunsi: If your CPU is recent enough (like Core2 or Nehalem), then `rdtsc` *is* a timesource. Nonstop and invariant TSC mean that it always ticks at constant frequency, regardless if the actual core clock is halted or running faster or slower than the rated / sticker frequency. See [wrong clock cycle measurements with rdtsc](https://stackoverflow.com/q/19941588) for some history. There was a dark age where RDTSC wasn't useful as a timesource or for cycle-accurate performance measurement, but that time has passed. Now it's only a timesource, and you need perf counters... – Peter Cordes Aug 18 '18 at 16:44
  • Another caveat is that cores on other sockets might not by synced perfectly. But within cores on a single CPU package, they are synced on Nehalem and later. [CPU TSC fetch operation especially in multicore-multi-processor environment](https://stackoverflow.com/q/10921210). See [Get CPU cycle count?](https://stackoverflow.com/a/51907627) for more links. – Peter Cordes Aug 18 '18 at 16:50
1

I have been happy with this on Linux:

inline double getFractionalSeconds(void) {
   struct timeval tv;   // see gettimeofday(2)
   gettimeofday(&tv, NULL);
   double t = (double) tv.tv_sec + (double) 1e-6 * tv.tv_usec; 
   // return seconds.microseconds since epoch 
   return(t);
}
Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
0

You could try the following code if you run on Intel

unsigned long long int rdtsc(void)
{
   unsigned long long int x;
   unsigned a, d;

   __asm__ volatile("rdtsc" : "=a" (a), "=d" (d));

   return ((unsigned long long)a) | (((unsigned long long)d) << 32);;
}
Douglas L
  • 116
  • 4