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
35 #define SNPRINTF_OR_RETURN(str, size, format, ...) { \
36 int len = snprintf((str), (size), (format), ## __VA_ARGS__); \
37 if (len < 0) return; \
38 if (len > static_cast<int>(size)) { \
39 fprintf(stderr, "Not enough space\n"); \
40 return; \
41 } else { \
42 (size) -= len; (str) += len; \
43 } \
44 }
45
46 namespace {
47 const bool kVerbose = false;
48 bool printRaw = false;
49 }
50
51 namespace android_test {
52
StopWatch(const char * name,size_t capacity)53 StopWatch::StopWatch(const char *name, size_t capacity)
54 : mName(strdup(name)), mNum(0), mData(NULL), mDataLen(0), mCapacity(capacity * 2),
55 mSizeKbytes(0), mAlreadyPrinted(false), mPrintRaw(false),
56 mDuration(0.0),
57 mMinDuration(0.0), mMinIdx(0),
58 mMaxDuration(0.0), mMaxIdx(0),
59 mDeltas(NULL), mUsed(false)
60 {
61 mStart.tv_sec = 0;
62 mStart.tv_nsec = 0;
63 mData = (Measurement *) malloc(mCapacity * sizeof(Measurement));
64 }
65
~StopWatch()66 StopWatch::~StopWatch()
67 {
68 if (mUsed && !mAlreadyPrinted)
69 {
70 fprintf(stderr, "Discarding data for %s\n", mName);
71 }
72 free(mData);
73 free(mName);
74 delete [] mDeltas;
75 }
76
start()77 void StopWatch::start()
78 {
79 checkCapacity();
80 clock_gettime(CLOCK_MONOTONIC, &mData[mDataLen].mTime);
81 mData[mDataLen].mIsStart = true;
82 if (!mUsed)
83 {
84 mStart = mData[mDataLen].mTime; // mDataLen should be 0
85 mUsed = true;
86 }
87 ++mNum;
88 ++mDataLen;
89 }
90
stop()91 void StopWatch::stop()
92 {
93 checkCapacity();
94 clock_gettime(CLOCK_MONOTONIC, &mData[mDataLen].mTime);
95 mData[mDataLen].mIsStart = false;
96 ++mDataLen;
97 }
98
setPrintRawMode(bool raw)99 void StopWatch::setPrintRawMode(bool raw)
100 {
101 printRaw = raw;
102 }
103
104
sprint(char ** str,size_t * size)105 void StopWatch::sprint(char **str, size_t *size)
106 {
107 if (kVerbose) fprintf(stderr, "printing\n");
108 mAlreadyPrinted = true;
109 if (0 == mDataLen)
110 {
111 return;
112 }
113 if (mDataLen > 0 && mData[mDataLen - 1].mIsStart)
114 {
115 stop();
116 }
117 if (kVerbose) SNPRINTF_OR_RETURN(*str, *size, "# Got %d samples for %s\n", mDataLen, mName);
118 processSamples();
119
120 SNPRINTF_OR_RETURN(*str, *size, "# StopWatch %s total/cumulative duration %f Samples: %d\n",
121 mName, mDuration, mNum);
122 printThroughput(str, size);
123 printAverageMinMax(str, size);
124
125 if (printRaw)
126 {
127 // print comment header and summary values.
128
129 SNPRINTF_OR_RETURN(*str, *size, "# Name Iterations Duration Min MinIdx Max MaxIdx SizeMbytes\n");
130 SNPRINTF_OR_RETURN(*str, *size, "%s %d %f %f %d %f %d %d\n", mName, mNum, mDuration,
131 mMinDuration, mMinIdx, mMaxDuration, mMaxIdx, mSizeKbytes);
132 // print each duration sample
133 for (size_t i = 0; i < mDataLen / 2; ++i)
134 {
135 long second = mData[i * 2].mTime.tv_sec - mStart.tv_sec;
136 long nano = mData[i * 2].mTime.tv_nsec - mStart.tv_nsec;
137
138 SNPRINTF_OR_RETURN(*str, *size, "%f %f\n", double(second) + double(nano) / 1.0e9, mDeltas[i]);
139 }
140 }
141
142 }
143
144 // Normally we should have enough capacity but if we have to
145 // reallocate the measurement buffer (e.g start and stop called more
146 // than once in an iteration) we let the user know. She should provide
147 // a capacity when building the StopWatch.
checkCapacity()148 void StopWatch::checkCapacity()
149 {
150 if (mDataLen >= mCapacity)
151 {
152 mCapacity *= 2;
153 fprintf(stderr, "# Increased capacity to %d for %s. Measurement affected.\n",
154 mCapacity, mName);
155 mData = (Measurement *)realloc(mData, mCapacity * sizeof(Measurement));
156 }
157 }
158
159
160 // Go over all the samples and compute the diffs between a start and
161 // stop pair. The diff is accumulated in mDuration and inserted in
162 // mDeltas.
163 // The min and max values for a diff are also tracked.
processSamples()164 void StopWatch::processSamples()
165 {
166 if (kVerbose) fprintf(stderr, "processing samples\n");
167 mDeltas= new double[mDataLen / 2];
168
169 for (size_t i = 0; i < mDataLen; i += 2) // even: start odd: stop
170 {
171 long second = mData[i + 1].mTime.tv_sec - mData[i].mTime.tv_sec;
172 long nano = mData[i + 1].mTime.tv_nsec - mData[i].mTime.tv_nsec;
173
174 mDeltas[i / 2] = double(second) + double(nano) / 1.0e9;
175 }
176
177 for (size_t i = 0; i < mDataLen / 2; ++i)
178 {
179 if (0 == i)
180 {
181 mMinDuration = mMaxDuration = mDeltas[i];
182 }
183 else
184 {
185 if (mMaxDuration < mDeltas[i])
186 {
187 mMaxDuration = mDeltas[i];
188 mMaxIdx = i;
189 }
190 if (mMinDuration > mDeltas[i])
191 {
192 mMinDuration = mDeltas[i];
193 mMinIdx = i;
194 }
195 }
196 mDuration += mDeltas[i];
197 }
198 }
199
timespecToDouble(const struct timespec & time)200 double StopWatch::timespecToDouble(const struct timespec& time)
201 {
202 double val = double(time.tv_nsec) / 1.0e9 + double(time.tv_sec);
203 return val < 0.0 ? -val : val; // sometimes 0.00 is -0.00
204 }
205
206
207 // If we have only 2 values, don't bother printing anything.
printAverageMinMax(char ** str,size_t * size)208 void StopWatch::printAverageMinMax(char **str, size_t *size)
209 {
210 if (mDataLen > 2) // if there is only one sample, avg, min, max are trivial.
211 {
212 SNPRINTF_OR_RETURN(*str, *size, "# Average %s duration %f s/op\n", mName, mDuration / mNum);
213 SNPRINTF_OR_RETURN(*str, *size, "# Min %s duration %f [%d]\n", mName, mMinDuration, mMinIdx);
214 SNPRINTF_OR_RETURN(*str, *size, "# Max %s duration %f [%d]\n", mName, mMaxDuration, mMaxIdx);
215 }
216 }
217
printThroughput(char ** str,size_t * size)218 void StopWatch::printThroughput(char **str, size_t *size)
219 {
220 if (0 != mSizeKbytes)
221 {
222 SNPRINTF_OR_RETURN(*str, *size, "# Size: %d Kbytes Total: %d\n", mSizeKbytes, mNum);
223 SNPRINTF_OR_RETURN(*str, *size, "# Speed %f Kbyte/s\n", double(mSizeKbytes) * mNum / mDuration);
224 }
225 }
226 } // namespace android_test
227