1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 30#include <malloc.h> 31#include <stdio.h> 32#include <time.h> 33#include "stopwatch.h" 34#include <math.h> 35 36#define SNPRINTF_OR_RETURN(str, size, format, ...) { \ 37 int len = snprintf((str), (size), (format), ## __VA_ARGS__); \ 38 if (len < 0) return; \ 39 if (len > static_cast<int>(size)) { \ 40 fprintf(stderr, "Not enough space\n"); \ 41 return; \ 42 } else { \ 43 (size) -= len; (str) += len; \ 44 } \ 45 } 46 47namespace { 48const bool kVerbose = false; 49bool printRaw = false; 50} 51 52namespace android_test { 53 54StopWatch::StopWatch(const char *name, size_t capacity) 55 : mName(strdup(name)), mNum(0), mData(NULL), mDataLen(0), mCapacity(capacity * 2), 56 mSizeKbytes(0), mAlreadyPrinted(false), mPrintRaw(false), 57 mDuration(0.0), mDeviation(0.0), 58 mMinDuration(0.0), mMinIdx(0), 59 mMaxDuration(0.0), mMaxIdx(0), 60 mDeltas(NULL), mUsed(false) 61{ 62 mStart.tv_sec = 0; 63 mStart.tv_nsec = 0; 64 mData = (Measurement *) malloc(mCapacity * sizeof(Measurement)); 65} 66 67StopWatch::~StopWatch() 68{ 69 if (mUsed && !mAlreadyPrinted) 70 { 71 fprintf(stderr, "Discarding data for %s\n", mName); 72 } 73 free(mData); 74 free(mName); 75 delete [] mDeltas; 76} 77 78void StopWatch::start() 79{ 80 checkCapacity(); 81 clock_gettime(CLOCK_MONOTONIC, &mData[mDataLen].mTime); 82 mData[mDataLen].mIsStart = true; 83 if (!mUsed) 84 { 85 mStart = mData[mDataLen].mTime; // mDataLen should be 0 86 mUsed = true; 87 } 88 ++mNum; 89 ++mDataLen; 90} 91 92void StopWatch::stop() 93{ 94 checkCapacity(); 95 clock_gettime(CLOCK_MONOTONIC, &mData[mDataLen].mTime); 96 mData[mDataLen].mIsStart = false; 97 ++mDataLen; 98} 99 100void StopWatch::setPrintRawMode(bool raw) 101{ 102 printRaw = raw; 103} 104 105 106void StopWatch::sprint(char **str, size_t *size) 107{ 108 if (kVerbose) fprintf(stderr, "printing\n"); 109 mAlreadyPrinted = true; 110 if (0 == mDataLen) 111 { 112 return; 113 } 114 if (mDataLen > 0 && mData[mDataLen - 1].mIsStart) 115 { 116 stop(); 117 } 118 if (kVerbose) SNPRINTF_OR_RETURN(*str, *size, "# Got %d samples for %s\n", mDataLen, mName); 119 processSamples(); 120 121 SNPRINTF_OR_RETURN(*str, *size, "# StopWatch %s total/cumulative duration %f Samples: %d\n", 122 mName, mDuration, mNum); 123 printThroughput(str, size); 124 printAverageMinMax(str, size); 125 126 if (printRaw) 127 { 128 // print comment header and summary values. 129 130 SNPRINTF_OR_RETURN(*str, *size, "# Name Iterations Duration Min MinIdx Max MaxIdx SizeKbytes\n"); 131 SNPRINTF_OR_RETURN(*str, *size, "%s %d %f %f %d %f %d %d\n", mName, mNum, mDuration, 132 mMinDuration, mMinIdx, mMaxDuration, mMaxIdx, mSizeKbytes); 133 // print each duration sample 134 for (size_t i = 0; i < mDataLen / 2; ++i) 135 { 136 long second = mData[i * 2].mTime.tv_sec - mStart.tv_sec; 137 long nano = mData[i * 2].mTime.tv_nsec - mStart.tv_nsec; 138 139 SNPRINTF_OR_RETURN(*str, *size, "%f %f\n", double(second) + double(nano) / 1.0e9, mDeltas[i]); 140 } 141 } 142 143} 144 145// Normally we should have enough capacity but if we have to 146// reallocate the measurement buffer (e.g start and stop called more 147// than once in an iteration) we let the user know. She should provide 148// a capacity when building the StopWatch. 149void StopWatch::checkCapacity() 150{ 151 if (mDataLen >= mCapacity) 152 { 153 mCapacity *= 2; 154 fprintf(stderr, "# Increased capacity to %d for %s. Measurement affected.\n", 155 mCapacity, mName); 156 mData = (Measurement *)realloc(mData, mCapacity * sizeof(Measurement)); 157 } 158} 159 160 161// Go over all the samples and compute the diffs between a start and 162// stop pair. The diff is accumulated in mDuration and inserted in 163// mDeltas. 164// The min and max values for a diff are also tracked. 165void StopWatch::processSamples() 166{ 167 if (kVerbose) fprintf(stderr, "processing samples\n"); 168 size_t n = mDataLen / 2; 169 mDeltas= new double[n]; 170 for (size_t i = 0; i < mDataLen; i += 2) // even: start odd: stop 171 { 172 long second = mData[i + 1].mTime.tv_sec - mData[i].mTime.tv_sec; 173 long nano = mData[i + 1].mTime.tv_nsec - mData[i].mTime.tv_nsec; 174 175 mDeltas[i / 2] = double(second) + double(nano) / 1.0e9; 176 } 177 178 for (size_t i = 0; i < n; ++i) 179 { 180 if (0 == i) 181 { 182 mMinDuration = mMaxDuration = mDeltas[i]; 183 } 184 else 185 { 186 if (mMaxDuration < mDeltas[i]) 187 { 188 mMaxDuration = mDeltas[i]; 189 mMaxIdx = i; 190 } 191 if (mMinDuration > mDeltas[i]) 192 { 193 mMinDuration = mDeltas[i]; 194 mMinIdx = i; 195 } 196 } 197 mDuration += mDeltas[i]; 198 } 199 double avgDuration = mDuration / n; 200 double diffSQ = 0.0; 201 for (size_t i = 0; i < n; ++i) 202 { 203 diffSQ += pow((mDeltas[i] - avgDuration), 2.0); 204 } 205 mDeviation = sqrt(diffSQ / n); 206} 207 208 209double StopWatch::timespecToDouble(const struct timespec& time) 210{ 211 double val = double(time.tv_nsec) / 1.0e9 + double(time.tv_sec); 212 return val < 0.0 ? -val : val; // sometimes 0.00 is -0.00 213} 214 215 216// If we have only 2 values, don't bother printing anything. 217void StopWatch::printAverageMinMax(char **str, size_t *size) 218{ 219 if (mDataLen > 2) // if there is only one sample, avg, min, max are trivial. 220 { 221 SNPRINTF_OR_RETURN(*str, *size, "# Average %s duration %f s/op\n", mName, mDuration / mNum); 222 SNPRINTF_OR_RETURN(*str, *size, "# Standard deviation %s duration %f \n", mName, mDeviation); 223 SNPRINTF_OR_RETURN(*str, *size, "# Min %s duration %f [%d]\n", mName, mMinDuration, mMinIdx); 224 SNPRINTF_OR_RETURN(*str, *size, "# Max %s duration %f [%d]\n", mName, mMaxDuration, mMaxIdx); 225 } 226} 227 228void StopWatch::printThroughput(char **str, size_t *size) 229{ 230 if (0 != mSizeKbytes) 231 { 232 SNPRINTF_OR_RETURN(*str, *size, "# Size: %d Kbytes Total: %d\n", mSizeKbytes, mNum); 233 SNPRINTF_OR_RETURN(*str, *size, "# Speed %f Kbyte/s\n", double(mSizeKbytes) * mNum / mDuration); 234 } 235} 236} // namespace android_test 237