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
This commit is contained in:
mtklein 2015-10-22 17:49:22 -07:00 committed by Commit bot
parent a1840d50e2
commit 5a06df4686
19 changed files with 817 additions and 45 deletions

View File

@ -40,7 +40,6 @@
[ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "chromeos"]', {
'link_settings': {
'libraries': [
'-lrt',
'-lpthread',
],
},

View File

@ -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',

View File

@ -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)

View File

@ -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 <time.h>
@ -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

View File

@ -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 <intrin.h>
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 <mach/mach_time.h>
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 <time.h>
double SkTime::GetNSecs() {
struct timespec ts = {0, 0};
(void)clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1e9 + ts.tv_nsec;
}
#endif

View File

@ -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
}

View File

@ -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 */
}

77
tools/timer/GpuTimer.cpp Normal file
View File

@ -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;
}
}

25
tools/timer/GpuTimer.h Normal file
View File

@ -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

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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);

220
tools/timer/TimerData.cpp Normal file
View File

@ -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;
}

85
tools/timer/TimerData.h Normal file
View File

@ -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