From 717a8a95142233636f738151dde5e1c8d9a7a705 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 21 Dec 2012 22:33:44 -0800 Subject: [PATCH] Add performance tests from Karma. --- tests/high_resolution_timer.hpp | 474 ++++++++++++++++++++++++++++++++ tests/int_generator.cpp | 129 +++++++++ 2 files changed, 603 insertions(+) create mode 100644 tests/high_resolution_timer.hpp create mode 100644 tests/int_generator.cpp diff --git a/tests/high_resolution_timer.hpp b/tests/high_resolution_timer.hpp new file mode 100644 index 00000000..16e926d5 --- /dev/null +++ b/tests/high_resolution_timer.hpp @@ -0,0 +1,474 @@ +// Copyright (c) 2005-2010 Hartmut Kaiser +// Copyright (c) 2009 Edward Grace +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(HIGH_RESOLUTION_TIMER_MAR_24_2008_1222PM) +#define HIGH_RESOLUTION_TIMER_MAR_24_2008_1222PM + +#include +#include + +#if defined(BOOST_HAS_UNISTD_H) +#include +#endif +#include + +#if defined(BOOST_WINDOWS) + +#include +#include +#include + +namespace util +{ + /////////////////////////////////////////////////////////////////////////////// + // + // high_resolution_timer + // A timer object measures elapsed time. + // CAUTION: Windows only! + // + /////////////////////////////////////////////////////////////////////////////// + class high_resolution_timer + { + public: + high_resolution_timer() + { + restart(); + } + + high_resolution_timer(double t) + { + LARGE_INTEGER frequency; + if (!QueryPerformanceFrequency(&frequency)) + boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); + + start_time.QuadPart = (LONGLONG)(t * frequency.QuadPart); + } + + high_resolution_timer(high_resolution_timer const& rhs) + : start_time(rhs.start_time) + { + } + + static double now() + { + SYSTEMTIME st; + GetSystemTime(&st); + + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + + LARGE_INTEGER now; + now.LowPart = ft.dwLowDateTime; + now.HighPart = ft.dwHighDateTime; + + // FileTime is in 100ns increments, result needs to be in [s] + return now.QuadPart * 1e-7; + } + + void restart() + { + if (!QueryPerformanceCounter(&start_time)) + boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); + } + double elapsed() const // return elapsed time in seconds + { + LARGE_INTEGER now; + if (!QueryPerformanceCounter(&now)) + boost::throw_exception(std::runtime_error("Couldn't get current time")); + + LARGE_INTEGER frequency; + if (!QueryPerformanceFrequency(&frequency)) + boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); + + return double(now.QuadPart - start_time.QuadPart) / frequency.QuadPart; + } + + double elapsed_max() const // return estimated maximum value for elapsed() + { + LARGE_INTEGER frequency; + if (!QueryPerformanceFrequency(&frequency)) + boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); + + return double((std::numeric_limits::max)() - start_time.QuadPart) / + double(frequency.QuadPart); + } + + double elapsed_min() const // return minimum value for elapsed() + { + LARGE_INTEGER frequency; + if (!QueryPerformanceFrequency(&frequency)) + boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); + + return 1.0 / frequency.QuadPart; + } + + private: + LARGE_INTEGER start_time; + }; + +} // namespace util + +#elif defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(_POSIX_THREAD_CPUTIME) + +#if _POSIX_THREAD_CPUTIME > 0 // timer always supported + +namespace util +{ + + /////////////////////////////////////////////////////////////////////////////// + // + // high_resolution_timer + // A timer object measures elapsed time. + // + /////////////////////////////////////////////////////////////////////////////// + class high_resolution_timer + { + public: + high_resolution_timer() + { + start_time.tv_sec = 0; + start_time.tv_nsec = 0; + + restart(); + } + + high_resolution_timer(double t) + { + start_time.tv_sec = time_t(t); + start_time.tv_nsec = (t - start_time.tv_sec) * 1e9; + } + + high_resolution_timer(high_resolution_timer const& rhs) + : start_time(rhs.start_time) + { + } + + static double now() + { + timespec now; + if (-1 == clock_gettime(CLOCK_REALTIME, &now)) + boost::throw_exception(std::runtime_error("Couldn't get current time")); + return double(now.tv_sec) + double(now.tv_nsec) * 1e-9; + } + + void restart() + { + if (-1 == clock_gettime(CLOCK_REALTIME, &start_time)) + boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); + } + double elapsed() const // return elapsed time in seconds + { + timespec now; + if (-1 == clock_gettime(CLOCK_REALTIME, &now)) + boost::throw_exception(std::runtime_error("Couldn't get current time")); + + if (now.tv_sec == start_time.tv_sec) + return double(now.tv_nsec - start_time.tv_nsec) * 1e-9; + + return double(now.tv_sec - start_time.tv_sec) + + (double(now.tv_nsec - start_time.tv_nsec) * 1e-9); + } + + double elapsed_max() const // return estimated maximum value for elapsed() + { + return double((std::numeric_limits::max)() - start_time.tv_sec); + } + + double elapsed_min() const // return minimum value for elapsed() + { + timespec resolution; + if (-1 == clock_getres(CLOCK_REALTIME, &resolution)) + boost::throw_exception(std::runtime_error("Couldn't get resolution")); + return double(resolution.tv_sec + resolution.tv_nsec * 1e-9); + } + + private: + timespec start_time; + }; + +} // namespace util + +#else // _POSIX_THREAD_CPUTIME > 0 + +#include + +// availability of high performance timers must be checked at runtime +namespace util +{ + /////////////////////////////////////////////////////////////////////////////// + // + // high_resolution_timer + // A timer object measures elapsed time. + // + /////////////////////////////////////////////////////////////////////////////// + class high_resolution_timer + { + public: + high_resolution_timer() + : use_backup(sysconf(_SC_THREAD_CPUTIME) <= 0) + { + if (!use_backup) { + start_time.tv_sec = 0; + start_time.tv_nsec = 0; + } + restart(); + } + + high_resolution_timer(double t) + : use_backup(sysconf(_SC_THREAD_CPUTIME) <= 0) + { + if (!use_backup) { + start_time.tv_sec = time_t(t); + start_time.tv_nsec = (t - start_time.tv_sec) * 1e9; + } + } + + high_resolution_timer(high_resolution_timer const& rhs) + : use_backup(sysconf(_SC_THREAD_CPUTIME) <= 0), + start_time(rhs.start_time) + { + } + + static double now() + { + if (sysconf(_SC_THREAD_CPUTIME) <= 0) + return double(std::clock()); + + timespec now; + if (-1 == clock_gettime(CLOCK_REALTIME, &now)) + boost::throw_exception(std::runtime_error("Couldn't get current time")); + return double(now.tv_sec) + double(now.tv_nsec) * 1e-9; + } + + void restart() + { + if (use_backup) + start_time_backup.restart(); + else if (-1 == clock_gettime(CLOCK_REALTIME, &start_time)) + boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); + } + double elapsed() const // return elapsed time in seconds + { + if (use_backup) + return start_time_backup.elapsed(); + + timespec now; + if (-1 == clock_gettime(CLOCK_REALTIME, &now)) + boost::throw_exception(std::runtime_error("Couldn't get current time")); + + if (now.tv_sec == start_time.tv_sec) + return double(now.tv_nsec - start_time.tv_nsec) * 1e-9; + + return double(now.tv_sec - start_time.tv_sec) + + (double(now.tv_nsec - start_time.tv_nsec) * 1e-9); + } + + double elapsed_max() const // return estimated maximum value for elapsed() + { + if (use_backup) + start_time_backup.elapsed_max(); + + return double((std::numeric_limits::max)() - start_time.tv_sec); + } + + double elapsed_min() const // return minimum value for elapsed() + { + if (use_backup) + start_time_backup.elapsed_min(); + + timespec resolution; + if (-1 == clock_getres(CLOCK_REALTIME, &resolution)) + boost::throw_exception(std::runtime_error("Couldn't get resolution")); + return double(resolution.tv_sec + resolution.tv_nsec * 1e-9); + } + + private: + bool use_backup; + timespec start_time; + boost::timer start_time_backup; + }; + +} // namespace util + +#endif // _POSIX_THREAD_CPUTIME > 0 + +#else // !defined(BOOST_WINDOWS) && (!defined(_POSIX_TIMERS) + // || _POSIX_TIMERS <= 0 + // || !defined(_POSIX_THREAD_CPUTIME) + // || _POSIX_THREAD_CPUTIME <= 0) + +#if defined(BOOST_HAS_GETTIMEOFDAY) + +// For platforms that do not support _POSIX_TIMERS but do have +// GETTIMEOFDAY, which is still preferable to std::clock() +#include +#include +#include + +namespace util +{ + + /////////////////////////////////////////////////////////////////////////// + // + // high_resolution_timer + // A timer object measures elapsed time. + // + // Implemented with gettimeofday() for platforms that support it, + // such as Darwin (OS X) but do not support the previous options. + // + // Copyright (c) 2009 Edward Grace + // + /////////////////////////////////////////////////////////////////////////// + class high_resolution_timer + { + private: + template + static inline double unsigned_diff(const U &a, const U &b) + { + if (a > b) + return static_cast(a-b); + return -static_cast(b-a); + } + + // @brief Return the difference between two timeval types. + // + // @param t1 The most recent timeval. + // @param t0 The historical timeval. + // + // @return The difference between the two in seconds. + double elapsed(const timeval &t1, const timeval &t0) const + { + if (t1.tv_sec == t0.tv_sec) + return unsigned_diff(t1.tv_usec,t0.tv_usec) * 1e-6; + + // We do it this way as the result of the difference of the + // microseconds can be negative if the clock is implemented so + // that the seconds timer increases in large steps. + // + // Naive subtraction of the unsigned types and conversion to + // double can wreak havoc! + return unsigned_diff(t1.tv_sec,t0.tv_sec) + + unsigned_diff(t1.tv_usec,t0.tv_usec) * 1e-6; + } + + public: + high_resolution_timer() + { + start_time.tv_sec = 0; + start_time.tv_usec = 0; + + restart(); + } + + high_resolution_timer(double t) + { + start_time.tv_sec = time_t(t); + start_time.tv_usec = (t - start_time.tv_sec) * 1e6; + } + + high_resolution_timer(high_resolution_timer const& rhs) + : start_time(rhs.start_time) + { + } + + static double now() + { + // Under some implementations gettimeofday() will always + // return zero. If it returns anything else however then + // we accept this as evidence of an error. Note we are + // not assuming that -1 explicitly indicates the error + // condition, just that non zero is indicative of the + // error. + timeval now; + if (gettimeofday(&now, NULL)) + boost::throw_exception(std::runtime_error("Couldn't get current time")); + return double(now.tv_sec) + double(now.tv_usec) * 1e-6; + } + + void restart() + { + if (gettimeofday(&start_time, NULL)) + boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); + } + + double elapsed() const // return elapsed time in seconds + { + timeval now; + if (gettimeofday(&now, NULL)) + boost::throw_exception(std::runtime_error("Couldn't get current time")); + return elapsed(now,start_time); + } + + double elapsed_max() const // return estimated maximum value for elapsed() + { + return double((std::numeric_limits::max)() - start_time.tv_sec); + } + + double elapsed_min() const // return minimum value for elapsed() + { + // On systems without an explicit clock_getres or similar + // we can only estimate an upper bound on the resolution + // by repeatedly calling the gettimeofday function. This + // is often likely to be indicative of the true + // resolution. + timeval t0, t1; + double delta(0); + + if (gettimeofday(&t0, NULL)) + boost::throw_exception(std::runtime_error("Couldn't get resolution.")); + + // Spin around in a tight loop until we observe a change + // in the reported timer value. + do { + if (gettimeofday(&t1, NULL)) + boost::throw_exception(std::runtime_error("Couldn't get resolution.")); + delta = elapsed(t1, t0); + } while (delta <= 0.0); + + return delta; + } + + private: + timeval start_time; + }; + +} + +#else // BOOST_HAS_GETTIMEOFDAY + +// For platforms other than Windows or Linux, or not implementing gettimeofday +// simply fall back to boost::timer +#include + +namespace util +{ + struct high_resolution_timer + : boost::timer + { + static double now() + { + return double(std::clock()); + } + }; +} + +#endif + +#endif + +#endif // HIGH_RESOLUTION_TIMER_AUG_14_2009_0425PM + +// +// $Log: high_resolution_timer.hpp,v $ +// Revision 1.4 2009/08/14 15:28:10 graceej +// * It is entirely possible for the updating clock to increment the +// * seconds and *decrement* the microseconds field. Consequently +// * when subtracting these unsigned microseconds fields a wrap-around +// * error can occur. For this reason elapsed(t1, t0) is used in a +// * similar maner to cycle.h this preserves the sign of the +// * difference. +// + diff --git a/tests/int_generator.cpp b/tests/int_generator.cpp new file mode 100644 index 00000000..7c822a2c --- /dev/null +++ b/tests/int_generator.cpp @@ -0,0 +1,129 @@ +// Copyright (c) 2001-2010 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include +#include + +#include "../high_resolution_timer.hpp" + +// This value specifies, how to unroll the integer string generation loop in +// Karma. +// Set this to some integer in between 0 (no unrolling) and max expected +// integer string len (complete unrolling). If not specified, this value +// defaults to 6. +#define BOOST_KARMA_NUMERICS_LOOP_UNROLL 6 + +#include + +using namespace std; +using namespace boost::spirit; + +#define MAX_ITERATION 10000000 + +/////////////////////////////////////////////////////////////////////////////// +struct random_fill +{ + int operator()() const + { + int scale = std::rand() / 100 + 1; + return (std::rand() * std::rand()) / scale; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + namespace karma = boost::spirit::karma; + + cout << "Converting " << MAX_ITERATION + << " randomly generated int values to strings." << flush << endl; + + std::srand(0); + std::vector v (MAX_ITERATION); + std::generate(v.begin(), v.end(), random_fill()); // randomly fill the vector + + // test the C libraries ltoa function (the most low level function for + // string conversion available) + { + //[karma_int_performance_ltoa + char buffer[65]; // we don't expect more than 64 bytes to be generated here + //<- + std::string str; + util::high_resolution_timer t; + //-> + for (int i = 0; i < MAX_ITERATION; ++i) + { + ltoa(v[i], buffer, 10); + //<- + str = buffer; // compensate for string ops in other benchmarks + //-> + } + //] + + cout << "ltoa:\t\t" << t.elapsed() << " [s]" << flush << endl; + } + + // test the iostreams library + { + //[karma_int_performance_iostreams + std::stringstream str; + //<- + util::high_resolution_timer t; + //-> + for (int i = 0; i < MAX_ITERATION; ++i) + { + str.str(""); + str << v[i]; + } + //] + + cout << "iostreams:\t" << t.elapsed() << " [s]" << flush << endl; + } + + // test the Boost.Format library + { + //[karma_int_performance_format + std::string str; + boost::format int_format("%d"); + //<- + util::high_resolution_timer t; + //-> + for (int i = 0; i < MAX_ITERATION; ++i) + { + str = boost::str(int_format % v[i]); + } + //] + + cout << "Boost.Format:\t" << t.elapsed() << " [s]" << flush << endl; + } + + // test the Karma int_ generation routines + { + std::string str; + util::high_resolution_timer t; + + //[karma_int_performance_plain + char buffer[65]; // we don't expect more than 64 bytes to be generated here + for (int i = 0; i < MAX_ITERATION; ++i) + { + char *ptr = buffer; + karma::generate(ptr, int_, v[i]); + *ptr = '\0'; + //<- + str = buffer; // compensate for string ops in other benchmarks + //-> + } + //] + + cout << "int_:\t\t" << t.elapsed() << " [s]" << flush << endl; + } + + return 0; +} +