stopwatch.cpp revision d6079c62819b4c022fe23e5101ca5382fbedb10f
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
46namespace {
47const bool kVerbose = false;
48bool printRaw = false;
49}
50
51namespace android_test {
52
53StopWatch::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
66StopWatch::~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
77void 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
91void StopWatch::stop()
92{
93    checkCapacity();
94    clock_gettime(CLOCK_MONOTONIC, &mData[mDataLen].mTime);
95    mData[mDataLen].mIsStart = false;
96    ++mDataLen;
97}
98
99void StopWatch::setPrintRawMode(bool raw)
100{
101    printRaw = raw;
102}
103
104
105void 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.
148void 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.
164void 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
200double 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.
208void 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
218void 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