include/time/cc_timer.h (75 lines of code) (raw):

/* * ccommon - a cache common library. * Copyright (C) 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #ifdef __cplusplus extern "C" { #endif #include <stdbool.h> #include <stdint.h> #include <time.h> struct duration; /* data structure to measure duration */ /* we declare duration and timeout in the header so static allocation is * possible. This is less than ideal from a clean abstraction point of view, * and the reason that a compromise is made for performance/efficiency reasons. * Mostly we want data structures related to time to be light-weight, so they * can be used whenever applicable, and the only way to avoid calling malloc * or equivalent, i.e. allocating on the stack, is to have static types. * * Users should NOT access these members directly. * * For duration: we use different declarations to stay in line with system APIs * * For timeouts: using uint64_t to represent the timeout timestamp seems * straightforward with most OSes. It is going to be slightly trickier with * Windows, which we currently don't support. * Reference: http://nadeausoftware.com/articles/2012/04/c_c_tip_how_measure_elapsed_real_time_benchmarking * * different implementations may interpret the tp (timestamp) field differently: * - on most POSIX-like platforms it means nanoseconds since an unspecified point; * - on OS X it means `mach time units' since an unspecified point, the * relationship between this unit and nanosecond can be obtained via another * syscall */ enum duration_type { DURATION_PRECISE, /* default */ DURATION_FAST, MAX_DURATION_TYPE }; #ifdef OS_DARWIN struct duration { bool started; bool stopped; uint64_t start; uint64_t stop; }; #elif defined OS_LINUX struct duration { enum duration_type type; bool started; bool stopped; struct timespec start; struct timespec stop; }; #endif struct timeout { int64_t tp; /* the timestamp */ bool is_set; /* * For now we are using a single struct to describe timeout in both the * absolute sense- "the event happens at 20:00:00 UTC", and the relative * sense- "the event happens after 5 minutes from now". The former reflects * how clock works and how timeouts are actually triggered, which deals with * time in the absolute sense (that is, after the starting point is chosen). * The latter reflects how caller defines timeout (5 minutes from now), * which has a constantly changing starting point. * * Other than libraries who are concerned with actually implementing the * timeouts (e.g., the timing wheel), most users should only use timeouts * in the relative sense. Users should set a timeout interval for their * timeout events, and submit that event to the corresponding library. */ bool is_intvl; }; /* update duration */ void duration_reset(struct duration *d); /* get a reading of duration and copy it without stopping the original timer */ void duration_snapshot(struct duration *s, const struct duration *d); void duration_start(struct duration *d); void duration_start_type(struct duration *d, enum duration_type type); void duration_stop(struct duration *d); /* read duration */ double duration_ns(struct duration *d); double duration_us(struct duration *d); double duration_ms(struct duration *d); double duration_sec(struct duration *d); static inline int duration_compare(const void *lhs, const void *rhs) { double lns = duration_ns((struct duration *)lhs); double rns = duration_ns((struct duration *)rhs); if (lns < rns) return -1; if (lns > rns) return 1; return 0; } /* * Not all possible granularity can be meaningfully used for sleep or event. * On many platforms, it's not realisitic to expect nanosecond-level expiration: * the system clock and scheduling granularity is simply too coarse. * * The internal presentation limits the maximum timeout duration set by these * functions- here user should assume expiration is within 2^64 nanoseconds from * the starting time of the monotonic clock. */ /* * I'm not a huge fan of getter/setter as a pattern, only when it makes sense: * 1) when the operations are non-trivial; * 2) when implementation details are platform-dependent. * * This is a case for 2). */ /* Question: do we have to worry about the overhead of getting timer values? */ void timeout_reset(struct timeout *e); /* update timeout */ void timeout_add_ns(struct timeout *e, uint64_t ns); void timeout_add_us(struct timeout *e, uint64_t us); void timeout_add_ms(struct timeout *e, uint64_t ms); void timeout_add_sec(struct timeout *e, uint64_t sec); void timeout_add_intvl(struct timeout *e, struct timeout *t); /* set the interval not absolute time */ void timeout_set_ns(struct timeout *e, uint64_t ns); void timeout_set_us(struct timeout *e, uint64_t us); void timeout_set_ms(struct timeout *e, uint64_t ms); void timeout_set_sec(struct timeout *e, uint64_t sec); /* return a timeout relative to an arbitrary timestamp, or sum/sub two intervals */ void timeout_sum_intvl(struct timeout *e, struct timeout *b, struct timeout *t); void timeout_sub_intvl(struct timeout *e, struct timeout *b, struct timeout *t); /* read timeout */ /* note we are using signed integer here: * - a positive return value shows the remaining time until timeout; * - a negative return value shows how long the timout is overdue. */ int64_t timeout_ns(struct timeout *e); int64_t timeout_us(struct timeout *e); int64_t timeout_ms(struct timeout *e); int64_t timeout_sec(struct timeout *e); /* Note: do not convert negative timeout values to timespec because it is * problematic to assign a negative value to timespec.tv_sec and use it with * certain Linux functions. See: https://lwn.net/Articles/394175/ */ void timeout_timespec(struct timespec *ts, struct timeout *e); bool timeout_expired(struct timeout *e); /* Note(yao): The return type of duration and timeout are different when * querying for the same quantity (esentially intervals), this is because I * envision them to be used differently: * - duration mostly is used for bookkeeping/stats, and it is commonly used as * the denominator to calculate average value or rate, and hence shouldn't be * rounded off prematurely, especially when user prefers a coarser unit such * as second; * - timeout is used to catch outliers (requests that have failed) or scheduled * activities (aggregation, flush file, log rotate). It is thus subject to the * time granularity of system scheduler, which is quite coarse compared to a * nanosecond. So it is more important to return a type that is easy for logic * operation (e.g. compare), while the precision of the return value is not * crucial. */ #ifdef __cplusplus } #endif