From 5a06df46864f010befaf8c57af1801fb43cbea86 Mon Sep 17 00:00:00 2001 From: mtklein Date: Thu, 22 Oct 2015 17:49:22 -0700 Subject: [PATCH] Revert of SkTime::GetNSecs() (patchset #10 id:180001 of https://codereview.chromium.org/1422513003/ ) Reason for revert: https://codereview.chromium.org/1412453006 Original issue's description: > SkTime::GetNSecs() > > - Move high-precision wall timers from tools/timer to SkTime. > - Implement SkTime::GetMSecs() in terms of SkTime::GetNSecs(). > - Delete unused tools/timer code. > > I have no idea what's going on there in src/animator. > I don't intend to investigate. > > BUG=skia: > > Committed: https://skia.googlesource.com/skia/+/70084cbc16ee8162649f2601377feb6e49de0217 > > CQ_EXTRA_TRYBOTS=client.skia.compile:Build-Ubuntu-GCC-x86_64-Debug-CrOS_Link-Trybot > > Committed: https://skia.googlesource.com/skia/+/a1840d50e29fd95e4df2d1168fe54c34c2a5c30c TBR=caryclark@google.com,reed@google.com,mtklein@chromium.org NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=skia: Review URL: https://codereview.chromium.org/1417753003 --- gyp/core.gyp | 1 - gyp/tools.gyp | 32 ++++- include/core/SkTime.h | 3 +- src/animator/SkTime.cpp | 18 +++ src/core/SkTime.cpp | 35 ----- src/ports/SkTime_Unix.cpp | 7 + src/ports/SkTime_win.cpp | 11 ++ tools/timer/GpuTimer.cpp | 77 +++++++++++ tools/timer/GpuTimer.h | 25 ++++ tools/timer/SysTimer_mach.cpp | 66 ++++++++++ tools/timer/SysTimer_mach.h | 24 ++++ tools/timer/SysTimer_posix.cpp | 51 +++++++ tools/timer/SysTimer_posix.h | 23 ++++ tools/timer/SysTimer_windows.cpp | 53 ++++++++ tools/timer/SysTimer_windows.h | 25 ++++ tools/timer/Timer.cpp | 46 +++++++ tools/timer/Timer.h | 60 ++++++++- tools/timer/TimerData.cpp | 220 +++++++++++++++++++++++++++++++ tools/timer/TimerData.h | 85 ++++++++++++ 19 files changed, 817 insertions(+), 45 deletions(-) create mode 100644 tools/timer/GpuTimer.cpp create mode 100644 tools/timer/GpuTimer.h create mode 100644 tools/timer/SysTimer_mach.cpp create mode 100644 tools/timer/SysTimer_mach.h create mode 100644 tools/timer/SysTimer_posix.cpp create mode 100644 tools/timer/SysTimer_posix.h create mode 100644 tools/timer/SysTimer_windows.cpp create mode 100644 tools/timer/SysTimer_windows.h create mode 100644 tools/timer/TimerData.cpp create mode 100644 tools/timer/TimerData.h diff --git a/gyp/core.gyp b/gyp/core.gyp index 5a46005678..2569a9478f 100644 --- a/gyp/core.gyp +++ b/gyp/core.gyp @@ -40,7 +40,6 @@ [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "chromeos"]', { 'link_settings': { 'libraries': [ - '-lrt', '-lpthread', ], }, diff --git a/gyp/tools.gyp b/gyp/tools.gyp index a4f8cfd62a..c25543ef2d 100644 --- a/gyp/tools.gyp +++ b/gyp/tools.gyp @@ -132,11 +132,39 @@ { 'target_name' : 'timer', 'type': 'static_library', - 'sources': [ '../tools/timer/Timer.cpp' ], + 'sources': [ + '../tools/timer/Timer.cpp', + '../tools/timer/TimerData.cpp', + ], + 'include_dirs': [ + '../include/private', + '../src/core', + '../src/gpu', + ], 'direct_dependent_settings': { 'include_dirs': ['../tools/timer'], }, - 'dependencies': [ 'skia_lib.gyp:skia_lib' ], + 'dependencies': [ + 'skia_lib.gyp:skia_lib', + 'jsoncpp.gyp:jsoncpp', + ], + 'conditions': [ + ['skia_gpu == 1', { + 'sources': [ '../tools/timer/GpuTimer.cpp' ], + }], + [ 'skia_os in ["mac", "ios"]', { + 'sources': [ '../tools/timer/SysTimer_mach.cpp' ], + }], + [ 'skia_os == "win"', { + 'sources': [ '../tools/timer/SysTimer_windows.cpp' ], + }], + [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "android", "chromeos"]', { + 'sources': [ '../tools/timer/SysTimer_posix.cpp' ], + }], + [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "chromeos"]', { + 'link_settings': { 'libraries': [ '-lrt' ] }, + }], + ], }, { 'target_name': 'skdiff', diff --git a/include/core/SkTime.h b/include/core/SkTime.h index 2cfe3efcb4..a8656347aa 100644 --- a/include/core/SkTime.h +++ b/include/core/SkTime.h @@ -34,8 +34,7 @@ public: }; static void GetDateTime(DateTime*); - static SkMSec GetMSecs() { return (SkMSec)(GetNSecs() * 1e-6); } - static double GetNSecs(); + static SkMSec GetMSecs(); }; #if defined(SK_DEBUG) && defined(SK_BUILD_FOR_WIN32) diff --git a/src/animator/SkTime.cpp b/src/animator/SkTime.cpp index 560ccd2fc8..a4e035bd1e 100644 --- a/src/animator/SkTime.cpp +++ b/src/animator/SkTime.cpp @@ -28,6 +28,15 @@ void SkTime::GetDateTime(DateTime* t) { } } +SkMSec SkTime::GetMSecs() { +#ifdef SK_DEBUG + if (gForceTickCount != (SkMSec) -1) { + return gForceTickCount; + } +#endif + return ::GetTickCount(); +} + #elif defined(xSK_BUILD_FOR_MAC) #include @@ -49,4 +58,13 @@ void SkTime::GetDateTime(DateTime* t) { } } +SkMSec SkTime::GetMSecs() { + UnsignedWide wide; + ::Microseconds(&wide); + + int64_t s = ((int64_t)wide.hi << 32) | wide.lo; + s = (s + 500) / 1000; // rounded divide + return (SkMSec)s; +} + #endif diff --git a/src/core/SkTime.cpp b/src/core/SkTime.cpp index 03970aa824..fa6c04447d 100644 --- a/src/core/SkTime.cpp +++ b/src/core/SkTime.cpp @@ -5,7 +5,6 @@ * found in the LICENSE file. */ -#include "SkOncePtr.h" #include "SkString.h" #include "SkTime.h" @@ -23,37 +22,3 @@ void SkTime::DateTime::toISO8601(SkString* dst) const { timeZoneMinutes); } } - -// TODO: use rdtscp when (runtime) available -// TODO: use std::chrono when (compile-time) available - -#if defined(_MSC_VER) - #include - SK_DECLARE_STATIC_ONCE_PTR(double, ns_per_tick); - double SkTime::GetNSecs() { - uint64_t ticks = __rdtsc(); - return ticks * *ns_per_tick.get([]{ - LARGE_INTEGER khz; // The docs say this returns Hz, but it returns KHz. - QueryPerformanceFrequency(&khz); - return new double(1e6 / khz.QuadPart); - }); - } -#elif defined(__MACH__) - #include - SK_DECLARE_STATIC_ONCE_PTR(double, ns_per_tick); - double SkTime::GetNSecs() { - uint64_t ticks = mach_absolute_time(); - return ticks * *ns_per_tick.get([]{ - mach_timebase_info_data_t timebase; - (void)mach_timebase_info(&timebase); - return new double(timebase.numer * 1.0 / timebase.denom); - }); - } -#else - #include - double SkTime::GetNSecs() { - struct timespec ts = {0, 0}; - (void)clock_gettime(CLOCK_MONOTONIC, &ts); - return ts.tv_sec * 1e9 + ts.tv_nsec; - } -#endif diff --git a/src/ports/SkTime_Unix.cpp b/src/ports/SkTime_Unix.cpp index 1ea3b8fbd0..ec96bb8a90 100644 --- a/src/ports/SkTime_Unix.cpp +++ b/src/ports/SkTime_Unix.cpp @@ -34,3 +34,10 @@ void SkTime::GetDateTime(DateTime* dt) dt->fSecond = SkToU8(tstruct->tm_sec); } } + +SkMSec SkTime::GetMSecs() +{ + struct timeval tv; + gettimeofday(&tv, nullptr); + return (SkMSec) (tv.tv_sec * 1000 + tv.tv_usec / 1000 ); // microseconds to milliseconds +} diff --git a/src/ports/SkTime_win.cpp b/src/ports/SkTime_win.cpp index 2bd5062c3c..19f4695a86 100644 --- a/src/ports/SkTime_win.cpp +++ b/src/ports/SkTime_win.cpp @@ -39,3 +39,14 @@ void SkTime::GetDateTime(DateTime* dt) dt->fSecond = SkToU8(st.wSecond); } } + +SkMSec SkTime::GetMSecs() +{ + FILETIME ft; + LARGE_INTEGER li; + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + __int64 t = li.QuadPart; /* In 100-nanosecond intervals */ + return (SkMSec)(t / 10000); /* In milliseconds */ +} diff --git a/tools/timer/GpuTimer.cpp b/tools/timer/GpuTimer.cpp new file mode 100644 index 0000000000..51ab3ad235 --- /dev/null +++ b/tools/timer/GpuTimer.cpp @@ -0,0 +1,77 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "GpuTimer.h" +#include "gl/SkGLContext.h" +#include "gl/GrGLUtil.h" + +GpuTimer::GpuTimer(const SkGLContext* glctx) : fContext(glctx) { + if (fContext) { + fContext->ref(); + fContext->makeCurrent(); + fStarted = false; + fSupported = GrGLGetVersion(fContext->gl()) > GR_GL_VER(3,3) || + fContext->gl()->hasExtension("GL_ARB_timer_query") || + fContext->gl()->hasExtension("GL_EXT_timer_query"); + + if (fSupported) { + SK_GL(*fContext, GenQueries(1, &fQuery)); + } + } +} + +GpuTimer::~GpuTimer() { + if (fContext) { + if (fSupported) { + fContext->makeCurrent(); + SK_GL(*fContext, DeleteQueries(1, &fQuery)); + } + fContext->unref(); + } +} + +void GpuTimer::start() { + if (fContext && fSupported) { + fContext->makeCurrent(); + fStarted = true; + SK_GL(*fContext, BeginQuery(GR_GL_TIME_ELAPSED, fQuery)); + } +} + +/** + * It is important to stop the cpu clocks first, + * as this will cpu wait for the gpu to finish. + */ +double GpuTimer::end() { + if (fContext && fSupported) { + fStarted = false; + fContext->makeCurrent(); + SK_GL(*fContext, EndQuery(GR_GL_TIME_ELAPSED)); + + GrGLint available = 0; + while (!available) { + SK_GL_NOERRCHECK(*fContext, GetQueryObjectiv(fQuery, + GR_GL_QUERY_RESULT_AVAILABLE, + &available)); + // If GetQueryObjectiv is erroring out we need some alternative + // means of breaking out of this loop + GrGLenum error; + SK_GL_RET_NOERRCHECK(*fContext, error, GetError()); + if (GR_GL_NO_ERROR != error) { + break; + } + } + GrGLuint64 totalGPUTimeElapsed = 0; + SK_GL(*fContext, GetQueryObjectui64v(fQuery, + GR_GL_QUERY_RESULT, + &totalGPUTimeElapsed)); + + return totalGPUTimeElapsed / 1000000.0; + } else { + return 0; + } +} diff --git a/tools/timer/GpuTimer.h b/tools/timer/GpuTimer.h new file mode 100644 index 0000000000..da1fdab5c0 --- /dev/null +++ b/tools/timer/GpuTimer.h @@ -0,0 +1,25 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef GpuTimer_DEFINED +#define GpuTimer_DEFINED + +class SkGLContext; + +class GpuTimer { +public: + GpuTimer(const SkGLContext*); + ~GpuTimer(); + void start(); + double end(); +private: + unsigned fQuery; + int fStarted; + const SkGLContext* fContext; + bool fSupported; +}; + +#endif diff --git a/tools/timer/SysTimer_mach.cpp b/tools/timer/SysTimer_mach.cpp new file mode 100644 index 0000000000..aca12dee52 --- /dev/null +++ b/tools/timer/SysTimer_mach.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SysTimer_mach.h" + +static time_value_t mac_cpu_time() { + mach_port_t task = mach_task_self(); + if (task == MACH_PORT_NULL) { + time_value_t none = {0, 0}; + return none; + } + + task_thread_times_info thread_info_data; + mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT; + if (KERN_SUCCESS != task_info(task, + TASK_THREAD_TIMES_INFO, + reinterpret_cast(&thread_info_data), + &thread_info_count)) { + time_value_t none = {0, 0}; + return none; + } + + time_value_add(&thread_info_data.user_time, &thread_info_data.system_time) + return thread_info_data.user_time; +} + +static double interval_in_ms(time_value_t start_clock, time_value_t end_clock) { + double duration_clock; + if ((end_clock.microseconds - start_clock.microseconds) < 0) { + duration_clock = (end_clock.seconds - start_clock.seconds-1) * 1000; + duration_clock += (1000000 + end_clock.microseconds - start_clock.microseconds) / 1000.0; + } else { + duration_clock = (end_clock.seconds - start_clock.seconds) * 1000; + duration_clock += (end_clock.microseconds - start_clock.microseconds) / 1000.0; + } + return duration_clock; +} + +void SysTimer::startWall() { + fStartWall = mach_absolute_time(); +} + +void SysTimer::startCpu() { + fStartCpu = mac_cpu_time(); +} + +double SysTimer::endCpu() { + time_value_t end_cpu = mac_cpu_time(); + return interval_in_ms(fStartCpu, end_cpu); +} + +double SysTimer::endWall() { + uint64_t end_wall = mach_absolute_time(); + + uint64_t elapsed = end_wall - fStartWall; + mach_timebase_info_data_t sTimebaseInfo; + if (KERN_SUCCESS != mach_timebase_info(&sTimebaseInfo)) { + return 0; + } else { + uint64_t elapsedNano = elapsed * sTimebaseInfo.numer / sTimebaseInfo.denom; + return elapsedNano / 1000000.0; + } +} diff --git a/tools/timer/SysTimer_mach.h b/tools/timer/SysTimer_mach.h new file mode 100644 index 0000000000..8c21d57b36 --- /dev/null +++ b/tools/timer/SysTimer_mach.h @@ -0,0 +1,24 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SysTimer_DEFINED +#define SysTimer_DEFINED + +#include +#include + +class SysTimer { +public: + void startWall(); + void startCpu(); + double endCpu(); + double endWall(); +private: + time_value_t fStartCpu; + uint64_t fStartWall; +}; + +#endif diff --git a/tools/timer/SysTimer_posix.cpp b/tools/timer/SysTimer_posix.cpp new file mode 100644 index 0000000000..4b7d708aab --- /dev/null +++ b/tools/timer/SysTimer_posix.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SysTimer_posix.h" + +static double interval_in_ms(timespec start_clock, timespec end_clock) +{ + double duration_clock; + if ((end_clock.tv_nsec - start_clock.tv_nsec) < 0) { + duration_clock = (end_clock.tv_sec - start_clock.tv_sec - 1) * 1000; + duration_clock += (1000000000 + end_clock.tv_nsec - start_clock.tv_nsec) / 1000000.0; + } else { + duration_clock = (end_clock.tv_sec - start_clock.tv_sec) * 1000; + duration_clock += (end_clock.tv_nsec - start_clock.tv_nsec) / 1000000.0; + } + return duration_clock; +} + +void SysTimer::startWall() { + if (-1 == clock_gettime(CLOCK_MONOTONIC, &fWall)) { + timespec none = {0, 0}; + fWall = none; + } +} +void SysTimer::startCpu() { + if (-1 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &fCpu)) { + timespec none = {0, 0}; + fCpu = none; + } +} + +double SysTimer::endCpu() { + timespec end_cpu; + if (-1 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_cpu)) { + timespec none = {0, 0}; + end_cpu = none; + } + return interval_in_ms(fCpu, end_cpu); +} + +double SysTimer::endWall() { + timespec end_wall; + if (-1 == clock_gettime(CLOCK_MONOTONIC, &end_wall)) { + timespec none = {0, 0}; + end_wall = none; + } + return interval_in_ms(fWall, end_wall); +} diff --git a/tools/timer/SysTimer_posix.h b/tools/timer/SysTimer_posix.h new file mode 100644 index 0000000000..1eca909e26 --- /dev/null +++ b/tools/timer/SysTimer_posix.h @@ -0,0 +1,23 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SysTimer_DEFINED +#define SysTimer_DEFINED + +#include + +class SysTimer { +public: + void startWall(); + void startCpu(); + double endCpu(); + double endWall(); +private: + timespec fCpu; + timespec fWall; +}; + +#endif diff --git a/tools/timer/SysTimer_windows.cpp b/tools/timer/SysTimer_windows.cpp new file mode 100644 index 0000000000..8e45b4a68e --- /dev/null +++ b/tools/timer/SysTimer_windows.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "SysTimer_windows.h" + +#include + +static ULONGLONG win_cpu_time() { + FILETIME createTime; + FILETIME exitTime; + FILETIME usrTime; + FILETIME sysTime; + if (0 == GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &sysTime, &usrTime)) { + return 0; + } + ULARGE_INTEGER start_cpu_sys; + ULARGE_INTEGER start_cpu_usr; + start_cpu_sys.LowPart = sysTime.dwLowDateTime; + start_cpu_sys.HighPart = sysTime.dwHighDateTime; + start_cpu_usr.LowPart = usrTime.dwLowDateTime; + start_cpu_usr.HighPart = usrTime.dwHighDateTime; + return start_cpu_sys.QuadPart + start_cpu_usr.QuadPart; +} + +void SysTimer::startCpu() { + fStartCpu = win_cpu_time(); +} + +double SysTimer::endCpu() { + ULONGLONG end_cpu = win_cpu_time(); + return static_cast(end_cpu - fStartCpu) / 10000.0L; +} + +// On recent Intel chips (roughly, "has Core or Atom in its name") __rdtsc will always tick +// at the CPU's maximum rate, even while power management clocks the CPU up and down. +// That's great, because it makes measuring wall time super simple. + +void SysTimer::startWall() { + fStartWall = __rdtsc(); +} + +double SysTimer::endWall() { + unsigned __int64 end = __rdtsc(); + + // This seems to, weirdly, give the CPU frequency in kHz. That's exactly what we want! + LARGE_INTEGER freq_khz; + QueryPerformanceFrequency(&freq_khz); + + return static_cast(end - fStartWall) / static_cast(freq_khz.QuadPart); +} diff --git a/tools/timer/SysTimer_windows.h b/tools/timer/SysTimer_windows.h new file mode 100644 index 0000000000..380debd313 --- /dev/null +++ b/tools/timer/SysTimer_windows.h @@ -0,0 +1,25 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef SysTimer_DEFINED +#define SysTimer_DEFINED + +//Time +#define WIN32_LEAN_AND_MEAN 1 +#include + +class SysTimer { +public: + void startWall(); + void startCpu(); + double endCpu(); + double endWall(); +private: + ULONGLONG fStartCpu; + unsigned __int64 fStartWall; +}; + +#endif diff --git a/tools/timer/Timer.cpp b/tools/timer/Timer.cpp index 28841cdc84..b59988ca26 100644 --- a/tools/timer/Timer.cpp +++ b/tools/timer/Timer.cpp @@ -6,6 +6,52 @@ */ #include "Timer.h" +Timer::Timer(SkGLContext* gl) + : fCpu(-1.0) + , fWall(-1.0) + , fTruncatedCpu(-1.0) + , fTruncatedWall(-1.0) + , fGpu(-1.0) +#if SK_SUPPORT_GPU + , fGpuTimer(gl) +#endif + {} + +void Timer::start() { + fSysTimer.startWall(); + fTruncatedSysTimer.startWall(); +#if SK_SUPPORT_GPU + fGpuTimer.start(); +#endif + fSysTimer.startCpu(); + fTruncatedSysTimer.startCpu(); +} + +void Timer::end() { + fCpu = fSysTimer.endCpu(); +#if SK_SUPPORT_GPU + //It is important to stop the cpu clocks first, + //as the following will cpu wait for the gpu to finish. + fGpu = fGpuTimer.end(); +#endif + fWall = fSysTimer.endWall(); +} + +void Timer::truncatedEnd() { + fTruncatedCpu = fTruncatedSysTimer.endCpu(); + fTruncatedWall = fTruncatedSysTimer.endWall(); +} + +WallTimer::WallTimer() : fWall(-1.0) {} + +void WallTimer::start() { + fSysTimer.startWall(); +} + +void WallTimer::end() { + fWall = fSysTimer.endWall(); +} + SkString HumanizeMs(double ms) { if (ms > 60e+3) return SkStringPrintf("%.3gm", ms/60e+3); if (ms > 1e+3) return SkStringPrintf("%.3gs", ms/1e+3); diff --git a/tools/timer/Timer.h b/tools/timer/Timer.h index 446eb254fd..9557596248 100644 --- a/tools/timer/Timer.h +++ b/tools/timer/Timer.h @@ -7,18 +7,68 @@ #ifndef Timer_DEFINED #define Timer_DEFINED -#include "SkString.h" -#include "SkTime.h" #include "SkTypes.h" +#include "SkString.h" +#if defined(SK_BUILD_FOR_WIN32) + #include "SysTimer_windows.h" +#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) + #include "SysTimer_mach.h" +#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) + #include "SysTimer_posix.h" +#endif + +#if SK_SUPPORT_GPU + #include "GpuTimer.h" +#endif + +class SkGLContext; + +/** + * SysTimers and GpuTimers are implemented orthogonally. + * This class combines 2 SysTimers and a GpuTimer into one single, + * platform specific Timer with a simple interface. The truncated + * timer doesn't include the time required for the GPU to finish + * its rendering. It should always be <= the un-truncated system + * times and (for GPU configurations) can be used to roughly (very + * roughly) gauge the GPU load/backlog. + */ +class Timer { +public: + explicit Timer(SkGLContext* gl = nullptr); + + void start(); + void truncatedEnd(); + void end(); + + // All times in milliseconds. + double fCpu; + double fWall; + double fTruncatedCpu; + double fTruncatedWall; + double fGpu; + +private: + SysTimer fSysTimer; + SysTimer fTruncatedSysTimer; +#if SK_SUPPORT_GPU + GpuTimer fGpuTimer; +#endif +}; + +// Same as Timer above, supporting only fWall but with much lower overhead. +// (Typically, ~30ns instead of Timer's ~1us.) class WallTimer { public: - WallTimer() : fWall(-1) {} + WallTimer(); - void start() { fWall = SkTime::GetNSecs(); } - void end() { fWall = (SkTime::GetNSecs() - fWall) * 1e-6; } + void start(); + void end(); double fWall; // Milliseconds. + +private: + SysTimer fSysTimer; }; SkString HumanizeMs(double); diff --git a/tools/timer/TimerData.cpp b/tools/timer/TimerData.cpp new file mode 100644 index 0000000000..d1460f73fd --- /dev/null +++ b/tools/timer/TimerData.cpp @@ -0,0 +1,220 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "TimerData.h" + +#include "Timer.h" +#include + +TimerData::TimerData(int maxNumTimings) + : fMaxNumTimings(maxNumTimings) + , fCurrTiming(0) + , fWallTimes(maxNumTimings) + , fTruncatedWallTimes(maxNumTimings) + , fCpuTimes(maxNumTimings) + , fTruncatedCpuTimes(maxNumTimings) + , fGpuTimes(maxNumTimings) {} + +bool TimerData::appendTimes(Timer* timer) { + SkASSERT(timer != nullptr); + if (fCurrTiming >= fMaxNumTimings) { + return false; + } + + fWallTimes[fCurrTiming] = timer->fWall; + fTruncatedWallTimes[fCurrTiming] = timer->fTruncatedWall; + fCpuTimes[fCurrTiming] = timer->fCpu; + fTruncatedCpuTimes[fCurrTiming] = timer->fTruncatedCpu; + fGpuTimes[fCurrTiming] = timer->fGpu; + + ++fCurrTiming; + + return true; +} + +SkString TimerData::getResult(const char* doubleFormat, + Result result, + const char *configName, + uint32_t timerFlags, + int itersPerTiming) { + SkASSERT(itersPerTiming >= 1); + + if (!fCurrTiming) { + return SkString(""); + } + + int numTimings = fCurrTiming; + + SkString wallStr(" msecs = "); + SkString truncWallStr(" Wmsecs = "); + SkString cpuStr(" cmsecs = "); + SkString truncCpuStr(" Cmsecs = "); + SkString gpuStr(" gmsecs = "); + + double wallMin = std::numeric_limits::max(); + double truncWallMin = std::numeric_limits::max(); + double cpuMin = std::numeric_limits::max(); + double truncCpuMin = std::numeric_limits::max(); + double gpuMin = std::numeric_limits::max(); + + double wallSum = 0; + double truncWallSum = 0; + double cpuSum = 0; + double truncCpuSum = 0; + double gpuSum = 0; + + for (int i = 0; i < numTimings; ++i) { + if (kPerIter_Result == result) { + wallStr.appendf(doubleFormat, fWallTimes[i] / itersPerTiming); + truncWallStr.appendf(doubleFormat, fTruncatedWallTimes[i] / itersPerTiming); + cpuStr.appendf(doubleFormat, fCpuTimes[i] / itersPerTiming); + truncCpuStr.appendf(doubleFormat, fTruncatedCpuTimes[i] / itersPerTiming); + gpuStr.appendf(doubleFormat, fGpuTimes[i] / itersPerTiming); + + if (i != numTimings - 1) { + static const char kSep[] = ", "; + wallStr.append(kSep); + truncWallStr.append(kSep); + cpuStr.append(kSep); + truncCpuStr.append(kSep); + gpuStr.append(kSep); + } + } else if (kMin_Result == result) { + wallMin = SkTMin(wallMin, fWallTimes[i]); + truncWallMin = SkTMin(truncWallMin, fTruncatedWallTimes[i]); + cpuMin = SkTMin(cpuMin, fCpuTimes[i]); + truncCpuMin = SkTMin(truncCpuMin, fTruncatedCpuTimes[i]); + gpuMin = SkTMin(gpuMin, fGpuTimes[i]); + } else { + SkASSERT(kAvg_Result == result); + wallSum += fWallTimes[i]; + truncWallSum += fTruncatedWallTimes[i]; + cpuSum += fCpuTimes[i]; + truncCpuSum += fTruncatedCpuTimes[i]; + } + + // We always track the GPU sum because whether it is non-zero indicates if valid gpu times + // were recorded at all. + gpuSum += fGpuTimes[i]; + } + + if (kMin_Result == result) { + wallStr.appendf(doubleFormat, wallMin / itersPerTiming); + truncWallStr.appendf(doubleFormat, truncWallMin / itersPerTiming); + cpuStr.appendf(doubleFormat, cpuMin / itersPerTiming); + truncCpuStr.appendf(doubleFormat, truncCpuMin / itersPerTiming); + gpuStr.appendf(doubleFormat, gpuMin / itersPerTiming); + } else if (kAvg_Result == result) { + int divisor = numTimings * itersPerTiming; + wallStr.appendf(doubleFormat, wallSum / divisor); + truncWallStr.appendf(doubleFormat, truncWallSum / divisor); + cpuStr.appendf(doubleFormat, cpuSum / divisor); + truncCpuStr.appendf(doubleFormat, truncCpuSum / divisor); + gpuStr.appendf(doubleFormat, gpuSum / divisor); + } + + SkString str; + str.printf(" %4s:", configName); + if (timerFlags & kWall_Flag) { + str += wallStr; + } + if (timerFlags & kTruncatedWall_Flag) { + str += truncWallStr; + } + if (timerFlags & kCpu_Flag) { + str += cpuStr; + } + if (timerFlags & kTruncatedCpu_Flag) { + str += truncCpuStr; + } + if ((timerFlags & kGpu_Flag) && gpuSum > 0) { + str += gpuStr; + } + return str; +} + +Json::Value TimerData::getJSON(uint32_t timerFlags, + Result result, + int itersPerTiming) { + SkASSERT(itersPerTiming >= 1); + Json::Value dataNode; + Json::Value wallNode, truncWall, cpuNode, truncCpu, gpuNode; + if (!fCurrTiming) { + return dataNode; + } + + int numTimings = fCurrTiming; + + double wallMin = std::numeric_limits::max(); + double truncWallMin = std::numeric_limits::max(); + double cpuMin = std::numeric_limits::max(); + double truncCpuMin = std::numeric_limits::max(); + double gpuMin = std::numeric_limits::max(); + + double wallSum = 0; + double truncWallSum = 0; + double cpuSum = 0; + double truncCpuSum = 0; + double gpuSum = 0; + + for (int i = 0; i < numTimings; ++i) { + if (kPerIter_Result == result) { + wallNode.append(fWallTimes[i] / itersPerTiming); + truncWall.append(fTruncatedWallTimes[i] / itersPerTiming); + cpuNode.append(fCpuTimes[i] / itersPerTiming); + truncCpu.append(fTruncatedCpuTimes[i] / itersPerTiming); + gpuNode.append(fGpuTimes[i] / itersPerTiming); + } else if (kMin_Result == result) { + wallMin = SkTMin(wallMin, fWallTimes[i]); + truncWallMin = SkTMin(truncWallMin, fTruncatedWallTimes[i]); + cpuMin = SkTMin(cpuMin, fCpuTimes[i]); + truncCpuMin = SkTMin(truncCpuMin, fTruncatedCpuTimes[i]); + gpuMin = SkTMin(gpuMin, fGpuTimes[i]); + } else { + SkASSERT(kAvg_Result == result); + wallSum += fWallTimes[i]; + truncWallSum += fTruncatedWallTimes[i]; + cpuSum += fCpuTimes[i]; + truncCpuSum += fTruncatedCpuTimes[i]; + } + + // We always track the GPU sum because whether it is non-zero indicates if valid gpu times + // were recorded at all. + gpuSum += fGpuTimes[i]; + } + + if (kMin_Result == result) { + wallNode.append(wallMin / itersPerTiming); + truncWall.append(truncWallMin / itersPerTiming); + cpuNode.append(cpuMin / itersPerTiming); + truncCpu.append(truncCpuMin / itersPerTiming); + gpuNode.append(gpuMin / itersPerTiming); + } else if (kAvg_Result == result) { + int divisor = numTimings * itersPerTiming; + wallNode.append(wallSum / divisor); + truncWall.append(truncWallSum / divisor); + cpuNode.append(cpuSum / divisor); + truncCpu.append(truncCpuSum / divisor); + gpuNode.append(gpuSum / divisor); + } + + if (timerFlags & kWall_Flag) { + dataNode["wall"] = wallNode; + } + if (timerFlags & kTruncatedWall_Flag) { + dataNode["truncWall"] = truncWall; + } + if (timerFlags & kCpu_Flag) { + dataNode["cpu"] = cpuNode; + } + if (timerFlags & kTruncatedCpu_Flag) { + dataNode["trucCpu"] = truncCpu; + } + if ((timerFlags & kGpu_Flag) && gpuSum > 0) { + dataNode["gpu"] = gpuNode; + } + return dataNode; +} diff --git a/tools/timer/TimerData.h b/tools/timer/TimerData.h new file mode 100644 index 0000000000..35e94dca1d --- /dev/null +++ b/tools/timer/TimerData.h @@ -0,0 +1,85 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef TimerData_DEFINED +#define TimerData_DEFINED + +#include "SkString.h" +#include "SkTemplates.h" + +#ifdef SK_BUILD_FOR_WIN + #pragma warning(push) + #pragma warning(disable : 4530) +#endif + +#include "SkJSONCPP.h" + +#ifdef SK_BUILD_FOR_WIN + #pragma warning(pop) +#endif + +class Timer; + +class TimerData { +public: + /** + * Constructs a TimerData to hold at most maxNumTimings sets of elapsed timer values. + **/ + explicit TimerData(int maxNumTimings); + + /** + * Collect times from the Timer for an iteration. It will fail if called more often than + * indicated in the constructor. + * + * @param Timer Must not be null. + */ + bool appendTimes(Timer*); + + enum Result { + kMin_Result, + kAvg_Result, + kPerIter_Result + }; + + enum TimerFlags { + kWall_Flag = 0x1, + kTruncatedWall_Flag = 0x2, + kCpu_Flag = 0x4, + kTruncatedCpu_Flag = 0x8, + kGpu_Flag = 0x10 + }; + + /** + * Gets the timer data results as a string. + * @param doubleFormat printf-style format for doubles (e.g. "%02d") + * @param result the type of result desired + * @param the name of the config being timed (prepended to results string) + * @param timerFlags bitfield of TimerFlags values indicating which timers should be reported. + * @param itersPerTiming the number of test/bench iterations that correspond to each + * appendTimes() call, 1 when appendTimes is called for each iteration. + */ + SkString getResult(const char* doubleFormat, + Result result, + const char* configName, + uint32_t timerFlags, + int itersPerTiming = 1); + Json::Value getJSON(uint32_t timerFlags, + Result result, + int itersPerTiming = 1); + +private: + int fMaxNumTimings; + int fCurrTiming; + + SkAutoTArray fWallTimes; + SkAutoTArray fTruncatedWallTimes; + SkAutoTArray fCpuTimes; + SkAutoTArray fTruncatedCpuTimes; + SkAutoTArray fGpuTimes; +}; + +#endif // TimerData_DEFINED