Chromium Code Reviews| Index: Source/web/ImageDecodeBench.cpp |
| diff --git a/Source/web/ImageDecodeBench.cpp b/Source/web/ImageDecodeBench.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7566e0cdcad07aec5724005c311d8bd6797751a6 |
| --- /dev/null |
| +++ b/Source/web/ImageDecodeBench.cpp |
| @@ -0,0 +1,263 @@ |
| +/* |
| + * Copyright (C) 2013 Google Inc. 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. |
| + * * Neither the name of Google Inc. nor the names of its |
| + * contributors may be used to endorse or promote products derived from |
| + * this software without specific prior written permission. |
| + * |
| + * 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 "config.h" |
| + |
| +#include "RuntimeEnabledFeatures.h" |
| +#include "core/platform/SharedBuffer.h" |
| +#include "core/platform/image-decoders/ImageDecoder.h" |
| +#include "public/web/WebKit.h" |
| +#include "wtf/OwnArrayPtr.h" |
| +#include "wtf/OwnPtr.h" |
| +#include "wtf/PassOwnPtr.h" |
| + |
| +#if OS(WINDOWS) |
| +#include <mmsystem.h> |
| +#include <time.h> |
| +#else |
| +#include <sys/time.h> |
| +#endif |
| + |
| +using namespace WebCore; |
| + |
| +#if OS(WINDOWS) |
| + |
| +// There's no real platform support herein, so adopt the win32 performance counter from WTF. |
| +// http://trac.webkit.org/browser/trunk/Source/WTF/wtf/CurrentTime.cpp?rev=152438 |
| + |
| +static double lowResUTCTime() |
| +{ |
| + FILETIME fileTime; |
| + GetSystemTimeAsFileTime(&fileTime); |
| + |
| + // As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a |
| + // ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can |
| + // prevent alignment faults on 64-bit Windows). |
| + ULARGE_INTEGER dateTime; |
| + memcpy(&dateTime, &fileTime, sizeof(dateTime)); |
| + |
| + // Number of 100 nanosecond between January 1, 1601 and January 1, 1970. |
| + static const ULONGLONG epochBias = 116444736000000000ULL; |
| + // Windows file times are in 100s of nanoseconds. |
| + static const double hundredsOfNanosecondsPerMillisecond = 10000; |
| + return (dateTime.QuadPart - epochBias) / hundredsOfNanosecondsPerMillisecond; |
| +} |
| + |
| +static LARGE_INTEGER qpcFrequency; |
| +static bool syncedTime; |
| + |
| +static double highResUpTime() |
| +{ |
| + // We use QPC, but only after sanity checking its result, due to bugs: |
| + // http://support.microsoft.com/kb/274323 http://support.microsoft.com/kb/895980 |
| + // http://msdn.microsoft.com/en-us/library/ms644904.aspx ("you can get different results |
| + // on different processors due to bugs in the basic input/output system (BIOS) or the |
| + // hardware abstraction layer (HAL)."). |
| + |
| + static LARGE_INTEGER qpcLast; |
| + static DWORD tickCountLast; |
| + static bool inited; |
| + |
| + LARGE_INTEGER qpc; |
| + QueryPerformanceCounter(&qpc); |
| + DWORD tickCount = GetTickCount(); |
| + |
| + if (inited) { |
| + __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart; |
| + __int64 tickCountElapsed; |
| + if (tickCount >= tickCountLast) { |
| + tickCountElapsed = (tickCount - tickCountLast); |
| + } else { |
| + __int64 tickCountLarge = tickCount + 0x100000000I64; |
| + tickCountElapsed = tickCountLarge - tickCountLast; |
| + } |
| + |
| + // Force a re-sync if QueryPerformanceCounter differs from GetTickCount() by more than |
| + // 500ms. (The 500ms value is from http://support.microsoft.com/kb/274323). |
| + __int64 diff = tickCountElapsed - qpcElapsed; |
| + if (diff > 500 || diff < -500) |
| + syncedTime = false; |
| + } else { |
| + inited = true; |
| + } |
| + |
| + qpcLast = qpc; |
| + tickCountLast = tickCount; |
| + |
| + return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart); |
| +} |
| + |
| +static bool qpcAvailable() |
| +{ |
| + static bool available; |
| + static bool checked; |
| + |
| + if (checked) |
| + return available; |
| + |
| + available = QueryPerformanceFrequency(&qpcFrequency); |
| + checked = true; |
| + return available; |
| +} |
| + |
| +static double getCurrentTime() |
| +{ |
| + // Use a combination of ftime and QueryPerformanceCounter. |
| + // ftime returns the information we want, but doesn't have sufficient resolution. |
| + // QueryPerformanceCounter has high resolution, but is only usable to measure time intervals. |
| + // To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will |
| + // use QueryPerformanceCounter by itself, adding the delta to the saved ftime. |
| + // We periodically re-sync to correct for drift. |
| + static double syncLowResUTCTime; |
| + static double syncHighResUpTime; |
| + static double lastUTCTime; |
| + |
| + double lowResTime = lowResUTCTime(); |
| + if (!qpcAvailable()) |
| + return lowResTime / 1000.0; |
| + |
| + double highResTime = highResUpTime(); |
| + if (!syncedTime) { |
| + timeBeginPeriod(1); // increase time resolution around low-res time getter |
| + syncLowResUTCTime = lowResTime = lowResUTCTime(); |
| + timeEndPeriod(1); // restore time resolution |
| + syncHighResUpTime = highResTime; |
| + syncedTime = true; |
| + } |
| + |
| + double highResElapsed = highResTime - syncHighResUpTime; |
| + double utc = syncLowResUTCTime + highResElapsed; |
| + |
| + // Force a clock re-sync if we've drifted. |
| + double lowResElapsed = lowResTime - syncLowResUTCTime; |
| + const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-res accuracy |
| + if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec) |
| + syncedTime = false; |
| + |
| + // Make sure time doesn't run backwards (only correct if the difference is < 2 seconds, |
| + // since DST or clock changes could occur). |
| + const double backwardTimeLimit = 2000.0; |
| + if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit) |
| + return lastUTCTime / 1000.0; |
| + lastUTCTime = utc; |
| + return utc / 1000.0; |
| +} |
| + |
| +#else |
| + |
| +static double getCurrentTime() |
| +{ |
| + struct timeval now; |
| + gettimeofday(&now, 0); |
| + return now.tv_sec + now.tv_usec / 1000000.0; |
| +} |
| + |
| +#endif |
| + |
| +static PassRefPtr<SharedBuffer> readFile(const char* fileName) |
| +{ |
| + FILE* fp = fopen(fileName, "rb"); |
| + if (!fp) { |
| + fprintf(stderr, "Can't open %s\n", fileName); |
| + exit(1); |
| + } |
| + |
| + fseek(fp, 0, SEEK_END); |
| + size_t fileSize = ftell(fp); |
| + rewind(fp); |
| + |
| + OwnArrayPtr<unsigned char> buffer = adoptArrayPtr(new unsigned char[fileSize]); |
| + fread(buffer.get(), 1, fileSize, fp); |
| + fclose(fp); |
| + |
| + return SharedBuffer::create(buffer.get(), fileSize); |
| +} |
| + |
| +static bool decodeImageData(SharedBuffer* data) |
| +{ |
| + OwnPtr<ImageDecoder> decoder = ImageDecoder::create(*data, |
| + ImageSource::AlphaPremultiplied, ImageSource::GammaAndColorProfileIgnored); |
| + decoder->setData(data, true); |
| + |
| + int frameCount = decoder->frameCount(); |
| + for (int i = 0; i < frameCount; ++i) { |
|
urvang (Google)
2013/07/29 22:38:36
If we always start decoding from the first frame,
|
| + if (!decoder->frameBufferAtIndex(i)) |
| + break; |
| + } |
| + |
| + return !decoder->failed(); |
| +} |
| + |
| +int main(int argc, char* argv[]) |
|
abarth-chromium
2013/07/23 05:44:42
This file should probably be in Source/testing or
|
| +{ |
| + if (argc < 2) { |
| + fprintf(stderr, "Usage: %s file [iterations]\n", argv[0]); |
| + exit(2); |
| + } |
| + |
| + size_t iterations = 1000; |
| + if (argc >= 3) { |
| + char* end = 0; |
| + iterations = strtol(argv[2], &end, 10); |
| + if (*end != '\0' || !iterations) { |
| + fprintf(stderr, "Second argument should be number of iterations. " |
| + "The default is 1000. You supplied %s\n", argv[2]); |
| + exit(2); |
| + } |
| + } |
| + |
| + class DummyWebKitPlatformSupport : public WebKit::Platform { |
| + public: |
| + const unsigned char* getTraceCategoryEnabledFlag(const char*) |
| + { return (const unsigned char *) "nope-none-nada"; } |
| + void cryptographicallyRandomValues(unsigned char*, size_t) { } |
| + }; |
| + |
| + WebKit::initializeWithoutV8(new DummyWebKitPlatformSupport()); |
| + WebCore::RuntimeEnabledFeatures::setAnimatedWebPEnabled(true); |
| + |
| + RefPtr<SharedBuffer> data = readFile(argv[1]); |
| + double totalTime = 0.0; |
| + |
| + for (size_t i = 0; i < iterations; ++i) { |
| + double startTime = getCurrentTime(); |
| + bool decoded = decodeImageData(data.get()); |
| + double elapsedTime = getCurrentTime() - startTime; |
| + totalTime += elapsedTime; |
| + if (!decoded) { |
| + fprintf(stderr, "Image decode failed.\n"); |
| + exit(3); |
| + } |
| + } |
| + |
| + double averageTime = totalTime / static_cast<double>(iterations); |
| + printf("%f %f\n", totalTime, averageTime); |
| + return 0; |
| +} |