| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| |
| #include <malloc.h> |
| #include <stdio.h> |
| #include <time.h> |
| #include "stopwatch.h" |
| #include <math.h> |
| |
| #define SNPRINTF_OR_RETURN(str, size, format, ...) { \ |
| int len = snprintf((str), (size), (format), ## __VA_ARGS__); \ |
| if (len < 0) return; \ |
| if (len > static_cast<int>(size)) { \ |
| fprintf(stderr, "Not enough space\n"); \ |
| return; \ |
| } else { \ |
| (size) -= len; (str) += len; \ |
| } \ |
| } |
| |
| namespace { |
| const bool kVerbose = false; |
| bool printRaw = false; |
| } |
| |
| namespace android_test { |
| |
| StopWatch::StopWatch(const char *name, size_t capacity) |
| : mName(strdup(name)), mNum(0), mData(NULL), mDataLen(0), mCapacity(capacity * 2), |
| mSizeKbytes(0), mAlreadyPrinted(false), mPrintRaw(false), |
| mDuration(0.0), mDeviation(0.0), |
| mMinDuration(0.0), mMinIdx(0), |
| mMaxDuration(0.0), mMaxIdx(0), |
| mDeltas(NULL), mUsed(false) |
| { |
| mStart.tv_sec = 0; |
| mStart.tv_nsec = 0; |
| mData = (Measurement *) malloc(mCapacity * sizeof(Measurement)); |
| } |
| |
| StopWatch::~StopWatch() |
| { |
| if (mUsed && !mAlreadyPrinted) |
| { |
| fprintf(stderr, "Discarding data for %s\n", mName); |
| } |
| free(mData); |
| free(mName); |
| delete [] mDeltas; |
| } |
| |
| void StopWatch::start() |
| { |
| checkCapacity(); |
| clock_gettime(CLOCK_MONOTONIC, &mData[mDataLen].mTime); |
| mData[mDataLen].mIsStart = true; |
| if (!mUsed) |
| { |
| mStart = mData[mDataLen].mTime; // mDataLen should be 0 |
| mUsed = true; |
| } |
| ++mNum; |
| ++mDataLen; |
| } |
| |
| void StopWatch::stop() |
| { |
| checkCapacity(); |
| clock_gettime(CLOCK_MONOTONIC, &mData[mDataLen].mTime); |
| mData[mDataLen].mIsStart = false; |
| ++mDataLen; |
| } |
| |
| void StopWatch::setPrintRawMode(bool raw) |
| { |
| printRaw = raw; |
| } |
| |
| |
| void StopWatch::sprint(char **str, size_t *size) |
| { |
| if (kVerbose) fprintf(stderr, "printing\n"); |
| mAlreadyPrinted = true; |
| if (0 == mDataLen) |
| { |
| return; |
| } |
| if (mDataLen > 0 && mData[mDataLen - 1].mIsStart) |
| { |
| stop(); |
| } |
| if (kVerbose) SNPRINTF_OR_RETURN(*str, *size, "# Got %d samples for %s\n", mDataLen, mName); |
| processSamples(); |
| |
| SNPRINTF_OR_RETURN(*str, *size, "# StopWatch %s total/cumulative duration %f Samples: %d\n", |
| mName, mDuration, mNum); |
| printThroughput(str, size); |
| printAverageMinMax(str, size); |
| |
| if (printRaw) |
| { |
| // print comment header and summary values. |
| |
| SNPRINTF_OR_RETURN(*str, *size, "# Name Iterations Duration Min MinIdx Max MaxIdx SizeKbytes\n"); |
| SNPRINTF_OR_RETURN(*str, *size, "%s %d %f %f %d %f %d %d\n", mName, mNum, mDuration, |
| mMinDuration, mMinIdx, mMaxDuration, mMaxIdx, mSizeKbytes); |
| // print each duration sample |
| for (size_t i = 0; i < mDataLen / 2; ++i) |
| { |
| long second = mData[i * 2].mTime.tv_sec - mStart.tv_sec; |
| long nano = mData[i * 2].mTime.tv_nsec - mStart.tv_nsec; |
| |
| SNPRINTF_OR_RETURN(*str, *size, "%f %f\n", double(second) + double(nano) / 1.0e9, mDeltas[i]); |
| } |
| } |
| |
| } |
| |
| // Normally we should have enough capacity but if we have to |
| // reallocate the measurement buffer (e.g start and stop called more |
| // than once in an iteration) we let the user know. She should provide |
| // a capacity when building the StopWatch. |
| void StopWatch::checkCapacity() |
| { |
| if (mDataLen >= mCapacity) |
| { |
| mCapacity *= 2; |
| fprintf(stderr, "# Increased capacity to %d for %s. Measurement affected.\n", |
| mCapacity, mName); |
| mData = (Measurement *)realloc(mData, mCapacity * sizeof(Measurement)); |
| } |
| } |
| |
| |
| // Go over all the samples and compute the diffs between a start and |
| // stop pair. The diff is accumulated in mDuration and inserted in |
| // mDeltas. |
| // The min and max values for a diff are also tracked. |
| void StopWatch::processSamples() |
| { |
| if (kVerbose) fprintf(stderr, "processing samples\n"); |
| size_t n = mDataLen / 2; |
| mDeltas= new double[n]; |
| for (size_t i = 0; i < mDataLen; i += 2) // even: start odd: stop |
| { |
| long second = mData[i + 1].mTime.tv_sec - mData[i].mTime.tv_sec; |
| long nano = mData[i + 1].mTime.tv_nsec - mData[i].mTime.tv_nsec; |
| |
| mDeltas[i / 2] = double(second) + double(nano) / 1.0e9; |
| } |
| |
| for (size_t i = 0; i < n; ++i) |
| { |
| if (0 == i) |
| { |
| mMinDuration = mMaxDuration = mDeltas[i]; |
| } |
| else |
| { |
| if (mMaxDuration < mDeltas[i]) |
| { |
| mMaxDuration = mDeltas[i]; |
| mMaxIdx = i; |
| } |
| if (mMinDuration > mDeltas[i]) |
| { |
| mMinDuration = mDeltas[i]; |
| mMinIdx = i; |
| } |
| } |
| mDuration += mDeltas[i]; |
| } |
| double avgDuration = mDuration / n; |
| double diffSQ = 0.0; |
| for (size_t i = 0; i < n; ++i) |
| { |
| diffSQ += pow((mDeltas[i] - avgDuration), 2.0); |
| } |
| mDeviation = sqrt(diffSQ / n); |
| } |
| |
| |
| double StopWatch::timespecToDouble(const struct timespec& time) |
| { |
| double val = double(time.tv_nsec) / 1.0e9 + double(time.tv_sec); |
| return val < 0.0 ? -val : val; // sometimes 0.00 is -0.00 |
| } |
| |
| |
| // If we have only 2 values, don't bother printing anything. |
| void StopWatch::printAverageMinMax(char **str, size_t *size) |
| { |
| if (mDataLen > 2) // if there is only one sample, avg, min, max are trivial. |
| { |
| SNPRINTF_OR_RETURN(*str, *size, "# Average %s duration %f s/op\n", mName, mDuration / mNum); |
| SNPRINTF_OR_RETURN(*str, *size, "# Standard deviation %s duration %f \n", mName, mDeviation); |
| SNPRINTF_OR_RETURN(*str, *size, "# Min %s duration %f [%d]\n", mName, mMinDuration, mMinIdx); |
| SNPRINTF_OR_RETURN(*str, *size, "# Max %s duration %f [%d]\n", mName, mMaxDuration, mMaxIdx); |
| } |
| } |
| |
| void StopWatch::printThroughput(char **str, size_t *size) |
| { |
| if (0 != mSizeKbytes) |
| { |
| SNPRINTF_OR_RETURN(*str, *size, "# Size: %d Kbytes Total: %d\n", mSizeKbytes, mNum); |
| SNPRINTF_OR_RETURN(*str, *size, "# Speed %f Kbyte/s\n", double(mSizeKbytes) * mNum / mDuration); |
| } |
| } |
| } // namespace android_test |