Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to solve issue #498: Replacing timer implementation #567

Merged
merged 2 commits into from Mar 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/mlpack/core/util/cli.cpp
Expand Up @@ -55,7 +55,7 @@ CLI::CLI(const CLI& other) : desc(other.desc),
CLI::~CLI()
{
// Terminate the program timers.
std::map<std::string, timeval>::iterator it;
std::map<std::string, std::chrono::microseconds>::iterator it;
for (it = timer.GetAllTimers().begin(); it != timer.GetAllTimers().end();
++it)
{
Expand All @@ -72,7 +72,7 @@ CLI::~CLI()
Print();

Log::Info << "Program timers:" << std::endl;
std::map<std::string, timeval>::iterator it;
std::map<std::string, std::chrono::microseconds>::iterator it;
for (it = timer.GetAllTimers().begin(); it != timer.GetAllTimers().end();
++it)
{
Expand Down
9 changes: 0 additions & 9 deletions src/mlpack/core/util/cli.hpp
Expand Up @@ -721,15 +721,6 @@ class CLI
*/
static std::string AliasReverseLookup(const std::string& value);

#ifdef _WIN32
/**
* Converts a FILETIME structure to an equivalent timeval structure.
* Only necessary on windows platforms.
* @param tv Valid timeval structure.
*/
void FileTimeToTimeVal(timeval* tv);
#endif

/**
* Checks that all required parameters have been specified on the command
* line. If any have not been specified, an error message is printed and the
Expand Down
186 changes: 35 additions & 151 deletions src/mlpack/core/util/timers.cpp
Expand Up @@ -14,19 +14,11 @@

using namespace mlpack;

// On Windows machines, we need to define timersub.
#ifdef _WIN32
inline void timersub(const timeval* tvp, const timeval* uvp, timeval* vvp)
inline std::chrono::microseconds getTimeDuration(const std::chrono::high_resolution_clock::time_point start,
const std::chrono::high_resolution_clock::time_point end)
{
vvp->tv_sec = tvp->tv_sec - uvp->tv_sec;
vvp->tv_usec = tvp->tv_usec - uvp->tv_usec;
if (vvp->tv_usec < 0)
{
--vvp->tv_sec;
vvp->tv_usec += 1000000;
}
return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
}
#endif

/**
* Start the given timer.
Expand All @@ -47,17 +39,17 @@ void Timer::Stop(const std::string& name)
/**
* Get the given timer.
*/
timeval Timer::Get(const std::string& name)
std::chrono::microseconds Timer::Get(const std::string& name)
{
return CLI::GetSingleton().timer.GetTimer(name);
}

std::map<std::string, timeval>& Timers::GetAllTimers()
std::map<std::string, std::chrono::microseconds>& Timers::GetAllTimers()
{
return timers;
}

timeval Timers::GetTimer(const std::string& timerName)
std::chrono::microseconds Timers::GetTimer(const std::string& timerName)
{
return timers[timerName];
}
Expand All @@ -69,49 +61,53 @@ bool Timers::GetState(std::string timerName)

void Timers::PrintTimer(const std::string& timerName)
{
timeval& t = timers[timerName];
Log::Info << t.tv_sec << "." << std::setw(6) << std::setfill('0')
<< t.tv_usec << "s";
std::chrono::microseconds totalDuration = timers[timerName];
// Converting microseconds to seconds
std::chrono::seconds totalDurationSec = std::chrono::duration_cast<std::chrono::seconds>(totalDuration);
std::chrono::microseconds totalDurationMicroSec = std::chrono::duration_cast<std::chrono::microseconds>(totalDuration % std::chrono::seconds(1));
Log::Info << totalDurationSec.count() << "." << std::setw(6) << std::setfill('0')
<< totalDurationMicroSec.count() << "s";

// Also output convenient day/hr/min/sec.
int days = t.tv_sec / 86400; // Integer division rounds down.
int hours = (t.tv_sec % 86400) / 3600;
int minutes = (t.tv_sec % 3600) / 60;
int seconds = (t.tv_sec % 60);
// The following line is a custom duration for a day
std::chrono::duration<int, std::ratio<60*60*24,1> > days = std::chrono::duration_cast<std::chrono::duration<int, std::ratio<60*60*24,1> > >(totalDuration); // Integer division rounds down.
std::chrono::hours hours = std::chrono::duration_cast<std::chrono::hours>(totalDuration % std::chrono::duration<int, std::ratio<60*60*24,1> >(1));
std::chrono::minutes minutes = std::chrono::duration_cast<std::chrono::minutes>(totalDuration % std::chrono::hours(1));
std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(totalDuration % std::chrono::minutes(1));
// No output if it didn't even take a minute.
if (!(days == 0 && hours == 0 && minutes == 0))
if (!(days.count() == 0 && hours.count() == 0 && minutes.count() == 0))
{
bool output = false; // Denotes if we have output anything yet.
Log::Info << " (";

// Only output units if they have nonzero values (yes, a bit tedious).
if (days > 0)
if (days.count() > 0)
{
Log::Info << days << " days";
Log::Info << days.count() << " days";
output = true;
}

if (hours > 0)
if (hours.count() > 0)
{
if (output)
Log::Info << ", ";
Log::Info << hours << " hrs";
Log::Info << hours.count() << " hrs";
output = true;
}

if (minutes > 0)
if (minutes.count() > 0)
{
if (output)
Log::Info << ", ";
Log::Info << minutes << " mins";
Log::Info << minutes.count() << " mins";
output = true;
}

if (seconds > 0)
if (seconds.count() > 0)
{
if (output)
Log::Info << ", ";
Log::Info << seconds << "." << std::setw(1) << (t.tv_usec / 100000) <<
Log::Info << seconds.count() << "." << std::setw(1) << (totalDurationMicroSec.count() / 100000) <<
"secs";
output = true;
}
Expand All @@ -122,90 +118,9 @@ void Timers::PrintTimer(const std::string& timerName)
Log::Info << std::endl;
}

void Timers::GetTime(timeval* tv)
std::chrono::high_resolution_clock::time_point Timers::GetTime()
{
#if defined(__MACH__) && defined(__APPLE__)

static mach_timebase_info_data_t info;

// If this is the first time we've run, get the timebase.
// We can use denom == 0 to indicate that sTimebaseInfo is
// uninitialised.
if (info.denom == 0) {
(void) mach_timebase_info(&info);
}

// Hope that the multiplication doesn't overflow.
uint64_t nsecs = mach_absolute_time() * info.numer / info.denom;
tv->tv_sec = nsecs / 1e9;
tv->tv_usec = (nsecs / 1e3) - (tv->tv_sec * 1e6);

#elif defined(_POSIX_VERSION)
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)

// Get the right clock_id.
#if defined(CLOCK_MONOTONIC_PRECISE)
static const clockid_t id = CLOCK_MONOTONIC_PRECISE;
#elif defined(CLOCK_MONOTONIC_RAW)
static const clockid_t id = CLOCK_MONOTONIC_RAW;
#elif defined(CLOCK_MONOTONIC)
static const clockid_t id = CLOCK_MONOTONIC;
#elif defined(CLOCK_REALTIME)
static const clockid_t id = CLOCK_REALTIME;
#else
static const clockid_t id = ((clockid_t) - 1);
#endif // CLOCK

struct timespec ts;

// Returns the current value tp for the specified clock_id.
if (clock_gettime(id, &ts) != -1 && id != ((clockid_t) - 1))
{
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / 1e3;
}

// Fallback for the clock_gettime function.
gettimeofday(tv, NULL);

#endif // _POSIX_TIMERS
#elif defined(_WIN32)

static double frequency = 0.0;
static LARGE_INTEGER offset;

// If this is the first time we've run, get the frequency.
// We use frequency == 0.0 to indicate that
// QueryPerformanceFrequency is uninitialised.
if (frequency == 0.0)
{
LARGE_INTEGER pF;
if (!QueryPerformanceFrequency(&pF))
{
// Fallback for the QueryPerformanceCounter function.
FileTimeToTimeVal(tv);
}
else
{
QueryPerformanceCounter(&offset);
frequency = (double)pF.QuadPart / 1000000.0;
}
}

if (frequency != 0.0)
{
LARGE_INTEGER pC;
// Get the current performance-counter value.
QueryPerformanceCounter(&pC);

pC.QuadPart -= offset.QuadPart;
double microseconds = (double)pC.QuadPart / frequency;
pC.QuadPart = microseconds;
tv->tv_sec = (long)pC.QuadPart / 1000000;
tv->tv_usec = (long)(pC.QuadPart % 1000000);
}

#endif
return std::chrono::high_resolution_clock::now();
}

void Timers::StartTimer(const std::string& timerName)
Expand All @@ -220,44 +135,16 @@ void Timers::StartTimer(const std::string& timerName)

timerState[timerName] = true;

timeval tmp;
tmp.tv_sec = 0;
tmp.tv_usec = 0;
std::chrono::high_resolution_clock::time_point currTime = GetTime();

GetTime(&tmp);

// Check to see if the timer already exists. If it does, we'll subtract the
// old value.
if (timers.count(timerName) == 1)
// If the timer is added first time
if (timers.count(timerName) == 0)
{
timeval tmpDelta;

timersub(&tmp, &timers[timerName], &tmpDelta);

tmp = tmpDelta;
timers[timerName] = (std::chrono::microseconds)0;
}

timers[timerName] = tmp;
}

#ifdef _WIN32
void Timers::FileTimeToTimeVal(timeval* tv)
{
FILETIME ftime;
uint64_t ptime = 0;
// Acquire the file time.
GetSystemTimeAsFileTime(&ftime);
// Now convert FILETIME to timeval.
ptime |= ftime.dwHighDateTime;
ptime = ptime << 32;
ptime |= ftime.dwLowDateTime;
ptime /= 10;
ptime -= DELTA_EPOCH_IN_MICROSECS;

tv->tv_sec = (long) (ptime / 1000000UL);
tv->tv_usec = (long) (ptime % 1000000UL);
timerStartTime[timerName] = currTime;
}
#endif // _WIN32

void Timers::StopTimer(const std::string& timerName)
{
Expand All @@ -271,11 +158,8 @@ void Timers::StopTimer(const std::string& timerName)

timerState[timerName] = false;

timeval delta, b, a = timers[timerName];

GetTime(&b);
std::chrono::high_resolution_clock::time_point currTime = GetTime();

// Calculate the delta time.
timersub(&b, &a, &delta);
timers[timerName] = delta;
timers[timerName] += getTimeDuration(timerStartTime[timerName], currTime);
}
48 changes: 10 additions & 38 deletions src/mlpack/core/util/timers.hpp
Expand Up @@ -10,46 +10,17 @@

#include <map>
#include <string>
#include <chrono> // chrono library for cross platform timer calculation

#if defined(__unix__) || defined(__unix)
#include <time.h> // clock_gettime()
#include <sys/time.h> // timeval, gettimeofday()
#include <unistd.h> // flags like _POSIX_VERSION
#elif defined(__MACH__) && defined(__APPLE__)
#include <mach/mach_time.h> // mach_timebase_info,
// mach_absolute_time()

// TEMPORARY
#include <time.h> // clock_gettime()
#include <sys/time.h> // timeval, gettimeofday()
#include <unistd.h> // flags like _POSIX_VERSION
#elif defined(_WIN32)
#ifndef NOMINMAX
#define NOMINMAX // Don't define min and max macros.
#endif
#include <windows.h> // GetSystemTimeAsFileTime(),
// QueryPerformanceFrequency(),
// QueryPerformanceCounter()
#include <winsock.h> // timeval on windows
#undef NOMINMAX

// uint64_t isn't defined on every windows.
#if defined(_WIN32)
// uint64_t isn't defined on every windows.
#if !defined(HAVE_UINT64_T)
#if SIZEOF_UNSIGNED_LONG == 8
typedef unsigned long uint64_t;
#else
typedef unsigned long long uint64_t;
#endif // SIZEOF_UNSIGNED_LONG
#endif // HAVE_UINT64_T

//gettimeofday has no equivalent will need to write extra code for that.
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif // _MSC_VER, _MSC_EXTENSIONS
#else
#error "unknown OS"
#endif

namespace mlpack {
Expand Down Expand Up @@ -90,7 +61,7 @@ class Timer
*
* @param name Name of timer to return value of.
*/
static timeval Get(const std::string& name);
static std::chrono::microseconds Get(const std::string& name);
};

class Timers
Expand All @@ -102,14 +73,14 @@ class Timers
/**
* Returns a copy of all the timers used via this interface.
*/
std::map<std::string, timeval>& GetAllTimers();
std::map<std::string, std::chrono::microseconds>& GetAllTimers();

/**
* Returns a copy of the timer specified.
*
* @param timerName The name of the timer in question.
*/
timeval GetTimer(const std::string& timerName);
std::chrono::microseconds GetTimer(const std::string& timerName);

/**
* Prints the specified timer. If it took longer than a minute to complete
Expand Down Expand Up @@ -146,12 +117,13 @@ class Timers

private:
//! A map of all the timers that are being tracked.
std::map<std::string, timeval> timers;
std::map<std::string, std::chrono::microseconds> timers;
//! A map that contains whether or not each timer is currently running.
std::map<std::string, bool> timerState;
//! A map for the starting values of the timers.
std::map<std::string, std::chrono::high_resolution_clock::time_point> timerStartTime;

void FileTimeToTimeVal(timeval* tv);
void GetTime(timeval* tv);
std::chrono::high_resolution_clock::time_point GetTime();
};

} // namespace mlpack
Expand Down