/* // Copyright (c) 2010-2017 Intel Corporation // // 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. */ #include "clock.h" #include #include #include #include "prox_compat.h" /* Calibrate TSC overhead by reading NB_READ times and take the smallest value. Bigger values are caused by external influence and can be discarded. The best estimate is the smallest read value. */ #define NB_READ 10000 uint32_t rdtsc_overhead; uint32_t rdtsc_overhead_stats; uint64_t thresh; uint64_t tsc_hz; /* calculate how much overhead is involved with calling rdtsc. This value has to be taken into account where the time spent running a small piece of code is measured */ static void init_tsc_overhead(void) { volatile uint32_t min_without_overhead = UINT32_MAX; volatile uint32_t min_with_overhead = UINT32_MAX; volatile uint32_t min_stats_overhead = UINT32_MAX; volatile uint64_t start1, end1; volatile uint64_t start2, end2; for (uint32_t i = 0; i < NB_READ; ++i) { start1 = rte_rdtsc(); end1 = rte_rdtsc(); start2 = rte_rdtsc(); end2 = rte_rdtsc(); end2 = rte_rdtsc(); if (min_without_overhead > end1 - start1) { min_without_overhead = end1 - start1; } if (min_with_overhead > end2 - start2) { min_with_overhead = end2 - start2; } } rdtsc_overhead = min_with_overhead - min_without_overhead; start1 = rte_rdtsc(); end1 = rte_rdtsc(); /* forbid the compiler to optimize this dummy variable */ volatile int dummy = 0; for (uint32_t i = 0; i < NB_READ; ++i) { start1 = rte_rdtsc(); dummy += 32; end1 = rte_rdtsc(); if (min_stats_overhead > end2 - start2) { min_stats_overhead = end1 - start1; } } rdtsc_overhead_stats = rdtsc_overhead + min_stats_overhead - min_without_overhead; } void clock_init(void) { init_tsc_overhead(); tsc_hz = rte_get_tsc_hz(); thresh = UINT64_MAX/tsc_hz; } uint64_t str_to_tsc(const char *from) { const uint64_t hz = rte_get_tsc_hz(); uint64_t ret; char str[16]; prox_strncpy(str, from, sizeof(str)); char *frac = strchr(str, '.'); if (frac) { *frac = 0; frac++; } ret = hz * atoi(str); if (!frac) return ret; uint64_t nsec = 0; uint64_t multiplier = 100000000; for (size_t i = 0; i < strlen(frac); ++i) { nsec += (frac[i] - '0') * multiplier; multiplier /= 10; } /* Wont overflow until CPU freq is ~18.44 GHz */ ret += hz * nsec/1000000000; return ret; } uint64_t sec_to_tsc(uint64_t sec) { if (sec < UINT64_MAX/rte_get_tsc_hz()) return sec * rte_get_tsc_hz(); else return UINT64_MAX; } uint64_t msec_to_tsc(uint64_t msec) { if (msec < UINT64_MAX/rte_get_tsc_hz()) return msec * rte_get_tsc_hz() / 1000; else return msec / 1000 * rte_get_tsc_hz(); } uint64_t usec_to_tsc(uint64_t usec) { if (usec < UINT64_MAX/rte_get_tsc_hz()) return usec * rte_get_tsc_hz() / 1000000; else return usec / 1000000 * rte_get_tsc_hz(); } uint64_t nsec_to_tsc(uint64_t nsec) { if (nsec < UINT64_MAX/rte_get_tsc_hz()) return nsec * rte_get_tsc_hz() / 1000000000; else return nsec / 1000000000 * rte_get_tsc_hz(); } uint64_t tsc_to_msec(uint64_t tsc) { if (tsc < UINT64_MAX / 1000) { return tsc * 1000 / rte_get_tsc_hz(); } else { return tsc / (rte_get_tsc_hz() / 1000); } } uint64_t tsc_to_usec(uint64_t tsc) { if (tsc < UINT64_MAX / 1000000) { return tsc * 1000000 / rte_get_tsc_hz(); } else { return tsc / (rte_get_tsc_hz() / 1000000); } } uint64_t tsc_to_nsec(uint64_t tsc) { if (tsc < UINT64_MAX / 1000000000) { return tsc * 1000000000 / rte_get_tsc_hz(); } else { return tsc / (rte_get_tsc_hz() / 1000000000); } } uint64_t tsc_to_sec(uint64_t tsc) { return tsc / rte_get_tsc_hz(); } struct time_unit tsc_to_time_unit(uint64_t tsc) { struct time_unit ret; uint64_t hz = rte_get_tsc_hz(); ret.sec = tsc/hz; ret.nsec = (tsc - ret.sec*hz)*1000000000/hz; return ret; } uint64_t time_unit_to_usec(struct time_unit *time_unit) { return time_unit->sec * 1000000 + time_unit->nsec/1000; } uint64_t time_unit_to_nsec(struct time_unit *time_unit) { return time_unit->sec * 1000000000 + time_unit->nsec; } int time_unit_cmp(struct time_unit *left, struct time_unit *right) { if (left->sec < right->sec) return -1; if (left->sec > right->sec) return 1; if (left->nsec < right->nsec) return -1; if (left->nsec > right->nsec) return -1; return 0; } uint64_t freq_to_tsc(uint64_t times_per_sec) { return rte_get_tsc_hz()/times_per_sec; } void tsc_to_tv(struct timeval *tv, const uint64_t tsc) { uint64_t hz = rte_get_tsc_hz(); uint64_t sec = tsc/hz; tv->tv_sec = sec; tv->tv_usec = ((tsc - sec * hz) * 1000000) / hz; } void tv_to_tsc(const struct timeval *tv, uint64_t *tsc) { uint64_t hz = rte_get_tsc_hz(); *tsc = tv->tv_sec * hz; *tsc += tv->tv_usec * hz / 1000000; } struct timeval tv_diff(const struct timeval *cur, const struct timeval *next) { uint64_t sec, usec; sec = next->tv_sec - cur->tv_sec; if (next->tv_usec < cur->tv_usec) { usec = next->tv_usec + 1000000 - cur->tv_usec; sec -= 1; } else usec = next->tv_usec - cur->tv_usec; struct timeval ret = { .tv_sec = sec, .tv_usec = usec, }; return ret; }