/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2016 Peter W. Draper (p.w.draper@durham.ac.uk)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
/**
* @file clocks.c
* @brief support for measuring intervals in milli seconds, when that
* is possible, otherwise ticks.
*
* Use cycle.h or timers.h for relative times.
*/
/* Config parameters. */
#include
/* Standard headers. */
#include
#include
#include
#include
/* Local headers. */
#include "clocks.h"
/* 0.25 of a second in nanoseconds. */
#define SLEEPTIME 250000000
/* The CPU frequency used to convert ticks to seconds. */
static unsigned long long clocks_cpufreq = 0;
/* Ticks when the CPU frequency was initialised, this marks the start of
* time. */
ticks clocks_start_ticks = 0;
/* The units of any returned times. */
static const char *clocks_units[] = {"ms", "~ms"};
static int clocks_units_index = 0;
static double clocks_units_scale = 1000.0;
/* Local prototypes. */
static void clocks_estimate_cpufreq(void);
/**
* @brief Get the current time.
*
* @param time the current time.
*/
void clocks_gettime(struct clocks_time *time) {
#ifdef HAVE_CLOCK_GETTIME
clock_gettime(CLOCK_REALTIME, &time->time);
#else
time->time = getticks();
#endif
}
/**
* @brief Get difference in between two times.
*
* @param start the start time.
* @param end the end time.
*
* @return the difference.
*/
double clocks_diff(struct clocks_time *start, struct clocks_time *end) {
#ifdef HAVE_CLOCK_GETTIME
struct timespec temp;
if ((end->time.tv_nsec - start->time.tv_nsec) < 0) {
temp.tv_sec = end->time.tv_sec - start->time.tv_sec - 1;
temp.tv_nsec = 1000000000 + end->time.tv_nsec - start->time.tv_nsec;
} else {
temp.tv_sec = end->time.tv_sec - start->time.tv_sec;
temp.tv_nsec = end->time.tv_nsec - start->time.tv_nsec;
}
return (double)temp.tv_sec * 1000.0 + (double)temp.tv_nsec * 1.0E-6;
#else
return elapsed(end->time, start->time) / clocks_get_cpufreq() *
clocks_units_scale;
#endif
}
/**
* @brief Set the CPU frequency.
*
* This function should be called at least once to set the CPU frequency.
* To use the builtin estimation techniques give a value of 0.
*
* @param freq the CPU frequency in Hz or 0 to estimate one.
*/
void clocks_set_cpufreq(unsigned long long freq) {
if (freq > 0) {
clocks_cpufreq = freq;
} else {
clocks_estimate_cpufreq();
}
clocks_start_ticks = getticks();
}
/**
* @brief Get the CPU frequency in Hz.
*
* @result the CPU frequency.
*/
unsigned long long clocks_get_cpufreq(void) {
if (clocks_cpufreq > 0) return clocks_cpufreq;
/* It not already set estimate it. */
clocks_estimate_cpufreq();
return clocks_cpufreq;
}
/**
* @brief Estimate the CPU frequency in Hz.
*
* If already set return the CPU frequency, then estimate the CPU frequency.
*
* The technique is either use a clock timed nanosleep (this was the best
* method on i7), to read the value from the cpuinfo_max_freq
* file (probably a overestimate) or finally just use a value of 1 with
* time units of ticks.
*/
static void clocks_estimate_cpufreq(void) {
#ifdef HAVE_CLOCK_GETTIME
/* Try to time a nanosleep() in ticks. */
struct clocks_time time1;
struct clocks_time time2;
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = SLEEPTIME;
clocks_gettime(&time1);
ticks tic = getticks();
/* Could do some calculation, but constant_tsc should protect us. */
nanosleep(&sleep, NULL);
clocks_gettime(&time2);
ticks toc = getticks();
double realsleep = clocks_diff(&time1, &time2);
clocks_cpufreq =
(signed long long)(double)(toc - tic) * 1.0 / realsleep * 1000.0;
clocks_units_index = 0;
clocks_units_scale = 1000.0;
#endif
/* Look for the system value, if available. Tends to be too large. */
#ifdef __linux__
if (clocks_cpufreq == 0) {
FILE *file =
fopen("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r");
if (file != NULL) {
unsigned long long maxfreq;
if (fscanf(file, "%llu", &maxfreq) == 1) {
clocks_cpufreq = maxfreq * 1000;
clocks_units_index = 0;
clocks_units_scale = 1000.0;
}
fclose(file);
}
}
#endif
/* If all fails just report ticks for a notional 2.6GHz machine. */
if (clocks_cpufreq == 0) {
unsigned long long maxfreq = 2600000;
clocks_cpufreq = maxfreq * 1000;
clocks_units_index = 1;
clocks_units_scale = 1000.0;
}
}
/**
* @brief Return the difference between two ticks.
*
* Only an approximation as based on how well we have estimated the
* rtc frequency. Should be good for machines that support constant_rtc
* and clock_gettime().
*
* @param tic a number of ticks returned by the cycle.h getticks() function.
* @param toc a number of ticks returned by the cycle.h getticks() function.
*
* @result the difference.
*/
double clocks_diff_ticks(ticks tic, ticks toc) {
return clocks_from_ticks(tic - toc);
}
/**
* @brief Convert a number of ticks into milli seconds, if possible.
*
* Only an approximation as based on how well we have estimated the
* rtc frequency. Should be good for machines that support constant_rtc
* and clock_gettime(), and reasonable for most Linux machines, otherwise
* ticks will just be returned. See clocks_getunit() for the actual units.
*
* @param tics a number of ticks returned by the cycle.h getticks() function.
*
* @result the milli seconds, if possible.
*/
double clocks_from_ticks(ticks tics) {
return ((double)tics / (double)clocks_get_cpufreq() * clocks_units_scale);
}
/**
* @brief Convert a number of milli seconds into ticks, if possible.
*
* Only an approximation as based on how well we have estimated the
* rtc frequency. Should be good for machines that support constant_rtc
* and clock_gettime(), and reasonable for most Linux machines, otherwise
* a guess will just be returned. See clocks_getunit() for the actual units.
*
* @param ms a number of "milliseconds" to convert to ticks
*
* @result the number of ticks, if possible.
*/
ticks clocks_to_ticks(double ms) {
return (ticks)(ms * (double)clocks_get_cpufreq() / clocks_units_scale);
}
/**
* @brief return the time units.
*
* Normally "ms" for milliseconds, but can be "ticks" when no conversion
* factor is available.
*
* @result the current time units.
*/
const char *clocks_getunit(void) { return clocks_units[clocks_units_index]; }
/**
* @brief returns the time since the start of the execution in seconds
*
* Need to call clocks_set_cpufreq() to mark the start of execution.
*
* The time is return in the format [sssss.s].
*
* @result the time since the start of the execution
*/
const char *clocks_get_timesincestart(void) {
static char buffer[40];
sprintf(buffer, "[%07.1f]",
clocks_diff_ticks(getticks(), clocks_start_ticks) / 1000.0);
return buffer;
}
/**
* Returns the wall-clock time since the start of execution in hours.
*
* Need to call clocks_set_cpufreq() to mark the start of execution.
*
* @result the time since the start of the execution
*/
double clocks_get_hours_since_start(void) {
return clocks_diff_ticks(getticks(), clocks_start_ticks) / (3600. * 1000.0);
}
/**
* @brief return the cpu times used.
*
* Uses the times(2) function to access the user and system cpu times and
* returns the sum of these for the process tree, i.e. current process plus
* "waited-for" children. This may be pthread implementation specific as to
* what that exactly means.
*
* Note cpu times are reported in sysconf(_SC_CLK_TCK) ticks, usually 100/s
* not our usual ticks.
*
* @param usertime the user time.
* @param systime the system time.
*/
void clocks_get_cputimes_used(double *usertime, double *systime) {
struct tms tmstic;
times(&tmstic);
*usertime = (tmstic.tms_utime + tmstic.tms_cutime);
*systime = (tmstic.tms_stime + tmstic.tms_cstime);
}
/**
* @brief Return an integer based on the current time.
*
* Normally this will be the remainder of the current number of nanoseconds
* so not very dissimilar in the most significant figures unless the time
* between calls is greater than INT_MAX nanoseconds. For faster calls use
* fewer figures, if that matters.
*
* @result an integer.
*/
int clocks_random_seed(void) {
#ifdef HAVE_CLOCK_GETTIME
struct timespec timespec;
clock_gettime(CLOCK_REALTIME, ×pec);
return (timespec.tv_nsec % INT_MAX);
#else
return (getticks() % INT_MAX);
#endif
}
/**
* @brief Get the current time, either in SWIFT format, "%T %F %Z"
* or seconds in the epoch.
* @param swift return SWIFT format.
* @result the formatted time, take a copy if you need to keep it longer than
* the next call.
*/
const char *clocks_now(int swift) {
static char now[64];
time_t tm = time(NULL);
struct tm *timeinfo = localtime(&tm);
if (swift) {
strftime(now, 64, "%T %F %Z", timeinfo);
} else {
strftime(now, 64, "%s", timeinfo);
}
return now;
}