From 4955da9ba5cb98115daa11e30d6a837959b5cbba Mon Sep 17 00:00:00 2001 From: bemandawes Date: Wed, 7 Sep 2011 13:24:21 +0000 Subject: [PATCH] Add benchmark.cpp and timer related support files git-svn-id: http://svn.boost.org/svn/boost/sandbox/endian@74296 b8fc166d-592f-0410-95f2-cb63ce0dd405 --- boost/endian/detail/config.hpp | 62 +++++++ boost/endian/support/timer.hpp | 118 +++++++++++++ libs/endian/support/run_timer.cpp | 132 ++++++++++++++ libs/endian/support/run_timer_ctors.cpp | 38 ++++ libs/endian/support/timer.cpp | 167 ++++++++++++++++++ libs/endian/test/benchmark.cpp | 107 +++++++++++ .../test/msvc10/benchmark/benchmark.vcxproj | 96 ++++++++++ libs/endian/test/msvc10/endian.sln | 6 + 8 files changed, 726 insertions(+) create mode 100644 boost/endian/detail/config.hpp create mode 100644 boost/endian/support/timer.hpp create mode 100644 libs/endian/support/run_timer.cpp create mode 100644 libs/endian/support/run_timer_ctors.cpp create mode 100644 libs/endian/support/timer.cpp create mode 100644 libs/endian/test/benchmark.cpp create mode 100644 libs/endian/test/msvc10/benchmark/benchmark.vcxproj diff --git a/boost/endian/detail/config.hpp b/boost/endian/detail/config.hpp new file mode 100644 index 0000000..8ece0a1 --- /dev/null +++ b/boost/endian/detail/config.hpp @@ -0,0 +1,62 @@ +// boost/endian/detail/config.hpp ----------------------------------------------------// + +// Copyright Beman Dawes 2003, 2010 + +// Distributed under the Boost Software License, Version 1.0. +// See http://www.boost.org/LICENSE_1_0.txt + +//--------------------------------------------------------------------------------------// + +#ifndef BOOST_ENDIAN_CONFIG_HPP +#define BOOST_ENDIAN_CONFIG_HPP + +// This header implements separate compilation features as described in +// http://www.boost.org/more/separate_compilation.html + +#include +#include // for BOOST_POSIX_API or BOOST_WINDOWS_API + +// throw an exception ----------------------------------------------------------------// +// +// Exceptions were originally thrown via boost::throw_exception(). +// As throw_exception() became more complex, it caused user error reporting +// to be harder to interpret, since the exception reported became much more complex. +// The immediate fix was to throw directly, wrapped in a macro to make any later change +// easier. + +#define BOOST_ENDIAN_THROW(EX) throw EX + +// enable dynamic linking -------------------------------------------------------------// + +#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_ENDIAN_DYN_LINK) +# if defined(BOOST_ENDIAN_SOURCE) +# define BOOST_ENDIAN_DECL BOOST_SYMBOL_EXPORT +# else +# define BOOST_ENDIAN_DECL BOOST_SYMBOL_IMPORT +# endif +#else +# define BOOST_ENDIAN_DECL +#endif + +// enable automatic library variant selection ----------------------------------------// + +#if !defined(BOOST_ENDIAN_SOURCE) && !defined(BOOST_ALL_NO_LIB) \ + && !defined(BOOST_ENDIAN_NO_LIB) +// +// Set the name of our library, this will get undef'ed by auto_link.hpp +// once it's done with it: +// +#define BOOST_LIB_NAME boost_endian +// +// If we're importing code from a dll, then tell auto_link.hpp about it: +// +#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_ENDIAN_DYN_LINK) +# define BOOST_DYN_LINK +#endif +// +// And include the header that does the work: +// +#include +#endif // auto-linking disabled + +#endif // BOOST_ENDIAN_CONFIG_HPP diff --git a/boost/endian/support/timer.hpp b/boost/endian/support/timer.hpp new file mode 100644 index 0000000..90c0cac --- /dev/null +++ b/boost/endian/support/timer.hpp @@ -0,0 +1,118 @@ +// boost timer.hpp ---------------------------------------------------------// + +// Copyright Beman Dawes 1994-2007 + +// Distributed under the Boost Software License, Version 1.0. +// See http://www.boost.org/LICENSE_1_0.txt + +// See http://www.boost.org/libs/system for documentation. + +#ifndef BOOST_ENDIAN_TIMER_HPP +#define BOOST_ENDIAN_TIMER_HPP + +#include + +#include +#include +#include +#include +#include +#include + +#include // must be the last #include + +namespace boost +{ + namespace endian + { + typedef boost::int_least64_t microsecond_t; + + struct times_t + { + microsecond_t wall; + microsecond_t user; + microsecond_t system; + + void clear() { wall = user = system = 0LL; } + }; + + // low-level functions -------------------------------------------------// + + BOOST_ENDIAN_DECL + void times(times_t& result); // throws on error + + BOOST_ENDIAN_DECL + system::error_code& times(times_t& result, system::error_code& ec); // never throws + + // timer ---------------------------------------------------------------// + + // unless otherwise specified, all functions throw on error + + class BOOST_ENDIAN_DECL timer + { + public: + + timer() : m_flags(m_stopped) { start(); } + timer(const std::nothrow_t&) : m_flags(static_cast(m_stopped + | m_nothrow)) { start(); } + ~timer() {} // never throws + + void start(); + const times_t& stop(); + bool stopped() const { return m_flags& m_stopped; } + void elapsed(times_t& result); // does not stop() + + private: + times_t m_times; + enum m_flags_t { m_stopped=1, m_nothrow=2 }; + m_flags_t m_flags; + }; + + // run_timer -----------------------------------------------------------// + + // unless otherwise specified, all functions throw on error + + class BOOST_ENDIAN_DECL run_timer : public timer + { + public: + + // each constructor has two overloads to avoid an explicit default to + // std::cout, which in turn would require including with its + // high associated cost even when the standard streams are not used. + + explicit run_timer(int places = 2); + + run_timer(int places, std::ostream& os) + : m_places(places), m_os(os), m_format(0) {} + + explicit run_timer(const std::string& format, int places = 2); + + run_timer(const std::string& format, int places, std::ostream& os) + : m_places(places), m_os(os), m_format(new char[format.size()+1]) + { std::strcpy(m_format, format.c_str()); } + + ~run_timer() // never throws + { + system::error_code ec; + if(!stopped()) + report(ec); + delete [] m_format; + } + + void report(); + system::error_code + report(system::error_code& ec); // never throws + + private: + int m_places; + std::ostream& m_os; + char* m_format; // doesn't use std::string as VC++ too painful + // across DLL boundaries due to warning C4251 + }; + + } // namespace endian +} // namespace boost + +#include // pops abi_prefix.hpp pragmas + +#endif // BOOST_ENDIAN_TIMER_HPP diff --git a/libs/endian/support/run_timer.cpp b/libs/endian/support/run_timer.cpp new file mode 100644 index 0000000..ed58f5a --- /dev/null +++ b/libs/endian/support/run_timer.cpp @@ -0,0 +1,132 @@ +// boost run_timer.cpp -----------------------------------------------------// + +// Copyright Beman Dawes 1994-2006 + +// Distributed under the Boost Software License, Version 1.0. +// See http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/system for documentation. + +//----------------------------------------------------------------------------// + +// define BOOST_ENDIAN_SOURCE so that knows +// the library is being built (possibly exporting rather than importing code) +#define BOOST_ENDIAN_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +using boost::endian::microsecond_t; +using boost::endian::times_t; +using boost::system::error_code; + +# if defined(BOOST_WINDOWS_API) +# include +# elif defined(BOOST_POSIX_API) +# include +# else +# error unknown API +# endif + +namespace +{ + const char * default_format = + " %ws wall, %us user + %ss system = %ts cpu (%p%)\n"; + +void show_time(const char * format, int places, std::ostream& os, + const times_t& times) + // NOTE WELL: Will truncate least-significant digits to LDBL_DIG, which may + // be as low as 10, although will be 15 for many common platforms. + { + if (times.wall < microsecond_t(0)) + return; + if (places > 6) + places = 6; + else if (places < 0) + places = 0; + + boost::io::ios_flags_saver ifs(os); + boost::io::ios_precision_saver ips(os); + os.setf(std::ios_base::fixed, std::ios_base::floatfield); + os.precision(places); + + const long double sec = 1000000.0L; + microsecond_t total = times.system + times.user; + long double wall_sec = times.wall / sec; + long double total_sec = total / sec; + + for (; *format; ++format) + { + if (*format != '%' || !*(format+1) || !std::strchr("wustp", *(format+1))) + os << *format; + else + { + ++format; + switch (*format) + { + case 'w': + os << times.wall / sec; + break; + case 'u': + os << times.user / sec; + break; + case 's': + os << times.system / sec; + break; + case 't': + os << total / sec; + break; + case 'p': + os.precision(1); + if (wall_sec > 0.001L && total_sec > 0.001L) + os << (total_sec/wall_sec) * 100.0; + else + os << "n/a"; + os.precision(places); + break; + default: + assert(0); + } + } + } + } + +} // unnamed namespace + +namespace boost +{ + namespace endian + { + // run_timer:: report --------------------------------------// + + void run_timer::report() + { + show_time(!m_format + ? default_format + : m_format, + m_places, m_os, this->stop()); + } + + error_code run_timer::report(error_code& ec) + { + try + { + report(); + ec = error_code(); + } + + catch (...) // eat any exceptions + { + ec = error_code(EIO, system::generic_category()); + } + + return ec; + } + + } // namespace endian +} // namespace boost diff --git a/libs/endian/support/run_timer_ctors.cpp b/libs/endian/support/run_timer_ctors.cpp new file mode 100644 index 0000000..4b3089e --- /dev/null +++ b/libs/endian/support/run_timer_ctors.cpp @@ -0,0 +1,38 @@ +// boost run_timer_ctors.cpp -----------------------------------------------// + +// Copyright Beman Dawes 2007 + +// Distributed under the Boost Software License, Version 1.0. +// See http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/system for documentation. + +//----------------------------------------------------------------------------// + +// These constructors are in a separate file so that this translation unit will +// not be linked in except when one of the constructors is actually used. This +// is important since header is required, and it incurs the cost of +// the standard stream objects even if they are not used. + +//----------------------------------------------------------------------------// + +// define BOOST_ENDIAN_SOURCE so that knows +// the library is being built (possibly exporting rather than importing code) +#define BOOST_ENDIAN_SOURCE + +#include +#include + +namespace boost +{ + namespace endian + { + run_timer::run_timer(int places) + : m_places(places), m_os(std::cout), m_format(0) {} + + run_timer::run_timer(const std::string& format, int places) + : m_places(places), m_os(std::cout), m_format(new char[format.size()+1]) + { std::strcpy(m_format, format.c_str()); } + + } // namespace endian +} // namespace boost diff --git a/libs/endian/support/timer.cpp b/libs/endian/support/timer.cpp new file mode 100644 index 0000000..db0a0f1 --- /dev/null +++ b/libs/endian/support/timer.cpp @@ -0,0 +1,167 @@ +// boost timer.cpp ---------------------------------------------------------// + +// Copyright Beman Dawes 1994-2006 + +// 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) + +// See http://www.boost.org/libs/system for documentation. + +//----------------------------------------------------------------------------// + +// define BOOST_ENDIAN_SOURCE so that knows +// the library is being built (possibly exporting rather than importing code) +#define BOOST_ENDIAN_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +# if defined(BOOST_WINDOWS_API) +# include +# elif defined(BOOST_POSIX_API) +# include +# include +# else +# error unknown API +# endif + +using boost::system::error_code; + +# if defined(BOOST_POSIX_API) +namespace +{ + long tick_factor() // multiplier to convert ticks + // to microseconds; -1 if unknown + { + static long tick_factor = 0; + if (!tick_factor) + { + if ((tick_factor = ::sysconf(_SC_CLK_TCK)) <= 0) + tick_factor = -1; + else + { + assert(tick_factor <= 1000000L); // doesn't handle large ticks + tick_factor = 1000000L / tick_factor; // compute factor + if (!tick_factor) tick_factor = -1; + } + } + return tick_factor; + } +} // unnamed namespace +# endif + +namespace boost +{ + namespace endian + { + + BOOST_ENDIAN_DECL + void times(times_t& current) + { + error_code ec; + if (times(current, ec)) + boost::throw_exception(system::system_error(ec, "boost::endian::times")); + } + + BOOST_ENDIAN_DECL + error_code& times(times_t& current, error_code& ec) + { + ec = error_code(); +# if defined(BOOST_WINDOWS_API) + ::GetSystemTimeAsFileTime((LPFILETIME)¤t.wall); + FILETIME creation, exit; + if (::GetProcessTimes(::GetCurrentProcess(), &creation, &exit, + (LPFILETIME)¤t.system, (LPFILETIME)¤t.user)) + { + current.wall /= 10; // Windows uses 100 nanosecond ticks + current.user /= 10; + current.system /= 10; + } + else + { + ec = error_code(::GetLastError(), system::system_category()); + current.wall = current.system = current.user = microsecond_t(-1); + } +# else + tms tm; + clock_t c = ::times(&tm); + if (c == -1) // error + { + ec = error_code(errno, system::system_category()); + current.wall = current.system = current.user = microsecond_t(-1); + } + else + { + current.wall = microsecond_t(c); + current.system = microsecond_t(tm.tms_stime + tm.tms_cstime); + current.user = microsecond_t(tm.tms_utime + tm.tms_cutime); + if (tick_factor() != -1) + { + current.wall *= tick_factor(); + current.user *= tick_factor(); + current.system *= tick_factor(); + } + else + { + ec = error_code(errno, system::system_category()); + current.wall = current.user = current.system = microsecond_t(-1); + } + } +# endif + return ec; + } + +#define BOOST_TIMES(C) \ + if (m_flags& m_nothrow) \ + { \ + error_code ec; \ + times(C, ec); \ + } \ + else \ + times(C); + + // timer ---------------------------------------------------------------// + + void timer::start() + { + m_flags = static_cast(m_flags& ~m_stopped); + BOOST_TIMES(m_times); + } + + const times_t& timer::stop() + { + if (stopped()) return m_times; + m_flags = static_cast(m_flags | m_stopped); + + times_t current; + BOOST_TIMES(current); + m_times.wall = (current.wall - m_times.wall); + m_times.user = (current.user - m_times.user); + m_times.system = (current.system - m_times.system); + return m_times; + } + + void timer::elapsed(times_t& current) + { + if (stopped()) + { + current.wall = m_times.wall; + current.user = m_times.user; + current.system = m_times.system; + } + else + { + BOOST_TIMES(current); + current.wall -= m_times.wall; + current.user -= m_times.user; + current.system -= m_times.system; + } + } + + } // namespace endian +} // namespace boost diff --git a/libs/endian/test/benchmark.cpp b/libs/endian/test/benchmark.cpp new file mode 100644 index 0000000..22c8031 --- /dev/null +++ b/libs/endian/test/benchmark.cpp @@ -0,0 +1,107 @@ +// benchmark.cpp ---------------------------------------------------------------------// + +// Copyright Beman Dawes 2011 + +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include + +using namespace boost; +using std::cout; +using std::cerr; +using std::endl; +using std::vector; + +#define BENCHMARK(Function) \ +{ \ + cout << "\nRunning benchmark..." << endl << ' '; \ + int64_t sum = 0; \ + int32_t value; \ + \ + endian::run_timer t; \ + \ + for (int32_t i = n; i; --i) \ + { \ + value = 0x01020304; \ + Function(value); \ + sum += value ; \ + } \ + \ + t.report(); \ + \ + cout << " Benchmark complete\n" \ + " sum is " << sum << endl; \ +} + +namespace +{ + std::string command_args; + long n; + long seed = 1; + int places = 2; + + void process_command_line(int argc, char * argv[]) + { + for (int a = 0; a < argc; ++a) + { + command_args += argv[a]; + if (a != argc-1) + command_args += ' '; + } + + cout << command_args << '\n';; + + if (argc >=2) + n = std::atol(argv[1]); + + for (; argc > 2; ++argv, --argc) + { + if ( *(argv[2]+1) == 'p' ) + places = atoi( argv[2]+2 ); + else + { + cout << "Error - unknown option: " << argv[2] << "\n\n"; + argc = -1; + break; + } + } + + if (argc < 2) + { + cout << "Usage: benchmark n [Options]\n" + " The argument n specifies the number of test cases to run\n" + " Options:\n" + " -p# Decimal places for times; default -p" << places << "\n"; + return std::exit(1); + } + } + + inline void noop(int32_t&) {} + + inline void shift_and_mask(int32_t& x) + { + x = ((x << 24) & 0xff000000) | ((x << 8) & 0x00ff0000) | ((x >> 24) & 0x000000ff) + | ((x >> 8) & 0x0000ff00); + } + +} // unnamed namespace + +//-------------------------------------- main() ---------------------------------------// + +int main(int argc, char * argv[]) +{ + process_command_line(argc, argv); + + BENCHMARK(noop); + BENCHMARK(endian::reorder); + BENCHMARK(shift_and_mask); + + return 0; +} diff --git a/libs/endian/test/msvc10/benchmark/benchmark.vcxproj b/libs/endian/test/msvc10/benchmark/benchmark.vcxproj new file mode 100644 index 0000000..3b33503 --- /dev/null +++ b/libs/endian/test/msvc10/benchmark/benchmark.vcxproj @@ -0,0 +1,96 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {C9FEAE75-4DD9-44F5-B302-9910559A91BE} + Win32Proj + benchmark + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + BOOST_ALL_NO_LIB;BOOST_ALL_DYN_LINK;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + d:\endian\stage\lib + + + "$(TargetDir)\$(TargetName).exe" 100 + + + + + Level3 + + + MaxSpeed + true + true + BOOST_ALL_NO_LIB;BOOST_ALL_DYN_LINK;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + d:\endian\stage\lib + + + "$(TargetDir)\$(TargetName).exe" 100000 + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/endian/test/msvc10/endian.sln b/libs/endian/test/msvc10/endian.sln index a519a12..92b7342 100644 --- a/libs/endian/test/msvc10/endian.sln +++ b/libs/endian/test/msvc10/endian.sln @@ -13,6 +13,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "endian_example", "endian_ex EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "conversion_test", "conversion_test\conversion_test.vcxproj", "{9FA33B0B-2B00-49E8-A892-E049D86076A9}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "benchmark", "benchmark\benchmark.vcxproj", "{C9FEAE75-4DD9-44F5-B302-9910559A91BE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -43,6 +45,10 @@ Global {9FA33B0B-2B00-49E8-A892-E049D86076A9}.Debug|Win32.Build.0 = Debug|Win32 {9FA33B0B-2B00-49E8-A892-E049D86076A9}.Release|Win32.ActiveCfg = Release|Win32 {9FA33B0B-2B00-49E8-A892-E049D86076A9}.Release|Win32.Build.0 = Release|Win32 + {C9FEAE75-4DD9-44F5-B302-9910559A91BE}.Debug|Win32.ActiveCfg = Debug|Win32 + {C9FEAE75-4DD9-44F5-B302-9910559A91BE}.Debug|Win32.Build.0 = Debug|Win32 + {C9FEAE75-4DD9-44F5-B302-9910559A91BE}.Release|Win32.ActiveCfg = Release|Win32 + {C9FEAE75-4DD9-44F5-B302-9910559A91BE}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE