Calculating elapsed time

Until this point we know that in every system there is a free-running, ever-incrementing counter, and all time is derived from it, be it wall time or any duration. The most natural idea here to calculate the time (seconds elapsed since the start of counter) would be dividing the number of cycles provided by this counter with the clock frequency, as expressed in the following formula:

Time (seconds) = (counter value)/(clock frequency)

There is a catch with this approach, however: it involves division (which works on an iterative algorithm, making it the slowest among the four basic arithmetic operations) and floating point calculations, which might be slower on certain architectures. While working with embedded platforms, floating point calculations are evidently slower than they are on PC or server platforms.

So how do we overcome this issue? Instead of division, time is calculated using multiplication and bitwise shift operations. The kernel provides a helper routine that derives the time this way. clocksource_cyc2ns(), defined in include/linux/clocksource.h, converts the clocksource cycles to nanoseconds:

static inline s64 clocksource_cyc2ns(u64 cycles, u32 mult, u32 shift)
{
        return ((u64) cycles * mult) >> shift;
}

Here, the parameter cycles is the number of elapsed cycles from the clock source, mult is the cycle-to-nanosecond multiplier, while shift is the cycle-to-nanosecond divisor (power of two). Both these parameters are clock source dependent. These values are provided by the clock source kernel abstraction discussed earlier.

Clock source hardware are not accurate all the time; their frequency might vary. This clock variation causes time drift (making the clock run faster or slower). In such cases, the variable mult can be adjusted to make up for this time drift.

The helper routine clocks_calc_mult_shift(), defined in kernel/time/clocksource.c, helps evaluate mult and shift factors:

void
clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec)
{
        u64 tmp;
        u32 sft, sftacc= 32;

        /*
        * Calculate the shift factor which is limiting the conversion
        * range:
        */
        tmp = ((u64)maxsec * from) >> 32;
        while (tmp) {
                tmp >>=1;
                sftacc--;
        }

        /*
        * Find the conversion shift/mult pair which has the best
        * accuracy and fits the maxsec conversion range:
        */
        for (sft = 32; sft > 0; sft--) {
                tmp = (u64) to << sft;
                tmp += from / 2;
                do_div(tmp, from);
                if ((tmp >> sftacc) == 0)
                        break;
        }
        *mult = tmp;
        *shift = sft;
}

Time duration between two events can be calculated as shown in the following code snippet:

struct clocksource *cs = &curr_clocksource;
cycle_t start = cs->read(cs);
/* things to do */
cycle_t end = cs->read(cs);
cycle_t diff = end – start;
duration = clocksource_cyc2ns(diff, cs->mult, cs->shift);
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.25.32