aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/timer
diff options
context:
space:
mode:
authorGravatar mtklein <mtklein@google.com>2015-10-22 17:49:22 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-10-22 17:49:22 -0700
commit5a06df46864f010befaf8c57af1801fb43cbea86 (patch)
tree2076a03c9e01fe3146e64755f54684abd3ea9497 /tools/timer
parenta1840d50e29fd95e4df2d1168fe54c34c2a5c30c (diff)
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
Diffstat (limited to 'tools/timer')
-rw-r--r--tools/timer/GpuTimer.cpp77
-rw-r--r--tools/timer/GpuTimer.h25
-rw-r--r--tools/timer/SysTimer_mach.cpp66
-rw-r--r--tools/timer/SysTimer_mach.h24
-rw-r--r--tools/timer/SysTimer_posix.cpp51
-rw-r--r--tools/timer/SysTimer_posix.h23
-rw-r--r--tools/timer/SysTimer_windows.cpp53
-rw-r--r--tools/timer/SysTimer_windows.h25
-rw-r--r--tools/timer/Timer.cpp46
-rw-r--r--tools/timer/Timer.h60
-rw-r--r--tools/timer/TimerData.cpp220
-rw-r--r--tools/timer/TimerData.h85
12 files changed, 750 insertions, 5 deletions
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<task_info_t>(&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 <mach/mach.h>
+#include <mach/mach_time.h>
+
+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 <time.h>
+
+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 <intrin.h>
+
+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<double>(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<double>(end - fStartWall) / static_cast<double>(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 <windows.h>
+
+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 <limits>
+
+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<double>::max();
+ double truncWallMin = std::numeric_limits<double>::max();
+ double cpuMin = std::numeric_limits<double>::max();
+ double truncCpuMin = std::numeric_limits<double>::max();
+ double gpuMin = std::numeric_limits<double>::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<double>::max();
+ double truncWallMin = std::numeric_limits<double>::max();
+ double cpuMin = std::numeric_limits<double>::max();
+ double truncCpuMin = std::numeric_limits<double>::max();
+ double gpuMin = std::numeric_limits<double>::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<double> fWallTimes;
+ SkAutoTArray<double> fTruncatedWallTimes;
+ SkAutoTArray<double> fCpuTimes;
+ SkAutoTArray<double> fTruncatedCpuTimes;
+ SkAutoTArray<double> fGpuTimes;
+};
+
+#endif // TimerData_DEFINED