pjlib系列之时间戳timestamp

时间戳结构体定义在types.h,本质上是两个32位加上一个64位。根据宏PJ_HAS_INT64来决定是用一个64位存储还是用2个32位存储,该结构体存储的单位是系统频率,比如系统频率是纳秒,则pj_timestamp.u32.lo = 10表示经过了10个周期,也就是10ns。

/**
 * This structure represents high resolution (64bit) time value. The time
 * values represent time in cycles, which is retrieved by calling
 * #pj_get_timestamp().
 */
typedef union pj_timestamp
{
    struct
    {
#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0
	pj_uint32_t lo;     /**< Low 32-bit value of the 64-bit value. */
	pj_uint32_t hi;     /**< high 32-bit value of the 64-bit value. */
#else
	pj_uint32_t hi;     /**< high 32-bit value of the 64-bit value. */
	pj_uint32_t lo;     /**< Low 32-bit value of the 64-bit value. */
#endif
    } u32;                  /**< The 64-bit value as two 32-bit values. */

#if PJ_HAS_INT64
    pj_uint64_t u64;        /**< The whole 64-bit value, where available. */
#endif
} pj_timestamp;

时间戳的实现在os_timestamp_common.c和os_timestamp_posix.c

其中os_timestamp_posix.c实现了不同类型的两个函数pj_get_timestamp和pj_get_timestamp_freq,其中纳秒级的实现本质上调用clock_gettime

#elif defined(USE_POSIX_TIMERS) && USE_POSIX_TIMERS != 0
#include <sys/time.h>
#include <time.h>
#include <errno.h>

#define NSEC_PER_SEC	1000000000

PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
{
    struct timespec tp;

    if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) {
	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
    }

    ts->u64 = tp.tv_sec;
    ts->u64 *= NSEC_PER_SEC;
    ts->u64 += tp.tv_nsec;

    return PJ_SUCCESS;
}

PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
{
    freq->u32.hi = 0;
    freq->u32.lo = NSEC_PER_SEC;

    return PJ_SUCCESS;
}

 微妙级实现

#else
#include <sys/time.h>
#include <errno.h>

#define USEC_PER_SEC	1000000

PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
{
    struct timeval tv;

    if (gettimeofday(&tv, NULL) != 0) {
	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
    }

    ts->u64 = tv.tv_sec;
    ts->u64 *= USEC_PER_SEC;
    ts->u64 += tv.tv_usec;

    return PJ_SUCCESS;
}

PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
{
    freq->u32.hi = 0;
    freq->u32.lo = USEC_PER_SEC;

    return PJ_SUCCESS;
}

os.h定义了几个时间戳加减比较的函数

(void) pj_set_timestamp32(pj_timestamp *t, pj_uint32_t hi, pj_uint32_t lo)
(int) pj_cmp_timestamp(const pj_timestamp *t1, const pj_timestamp *t2)
(void) pj_add_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
(void) pj_add_timestamp32(pj_timestamp *t1, pj_uint32_t t2)
(void) pj_sub_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
(void) pj_sub_timestamp32(pj_timestamp *t1, pj_uint32_t t2)
(pj_int32_t) pj_timestamp_diff32(const pj_timestamp *t1, const pj_timestamp *t2)

 os_timestamp_common.c定义了elapsed_xxx运行时间计算的函数。

(pj_time_val) pj_elapsed_time( const pj_timestamp *start, const pj_timestamp *stop )
(pj_uint32_t) pj_elapsed_msec( const pj_timestamp *start, const pj_timestamp *stop )
(pj_uint64_t) pj_elapsed_msec64(const pj_timestamp *start,const pj_timestamp *stop )
(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, const pj_timestamp *stop )
(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, const pj_timestamp *stop )
(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, const pj_timestamp *stop )

这里用到一个自定义结构体pj_highprec_t,定义在high_precision.h。它可以在编译时决定是64位,32位,还是double浮点型。

#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT != 0
    /*
     * The first choice for high precision math is to use double.
     */
#   include <math.h>
    typedef double pj_highprec_t;

#   define PJ_HIGHPREC_VALUE_IS_ZERO(a)     (a==0)
#   define pj_highprec_mod(a,b)             (a=fmod(a,b))

#elif defined(PJ_HAS_INT64) && PJ_HAS_INT64 != 0
    /*
     * Next choice is to use 64-bit arithmatics.
     */
    typedef pj_int64_t pj_highprec_t;

#else
#   warning "High precision math is not available"

    /*
     * Last, fallback to 32-bit arithmetics.
     */
    typedef pj_int32_t pj_highprec_t;

#endif

 

ecapsed_xxx函数都是先调用get_elapsed,计算运行了几个时间周期,然后再根据周期频率换算成毫秒或者微妙。ecapsed_xx只是起到计算起始到结束的运行时间,那么获取当前运行时间则是前面pj_get_timestamp。另外,pj_gettickcount先获取当前时间戳,再通过减去0,计算运行时间。

总结下Linux下时间使用场景,如果你只是要精确到秒或者更粗糙的时间,则time函数够用了。如果是程序中一般精确到毫秒,那么gettimeofday精度也够,但是容易因为系统时间的跳变而跳变。如果精度要求非常高,则使用clock_gettime(),其中CLOCK_MONOTONIC表示单调递增,虽然不会因为手动设置时间而跳变,但是受 adjtime(3) 影响。如果内核版本高于2.6.28,则可以使用CLOCK_MONOTONIC_RAW,真正的单调递增,不受adjtime(3) 影响。下面贴上man clock_gettime。


       CLOCK_MONOTONIC
              Clock  that cannot be set and represents monotonic time since some unspecified starting point.  This clock is not affected by
              discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), but is affected by the
              incremental adjustments performed by adjtime(3) and NTP.


       CLOCK_MONOTONIC_RAW (since Linux 2.6.28; Linux-specific)
              Similar  to  CLOCK_MONOTONIC,  but provides access to a raw hardware-based time that is not subject to NTP adjustments or the
              incremental adjustments performed by adjtime(3).
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值