/* ///////////////////////////////////////////////////////////////////////////// * File: perf.d * * Purpose: Performance measurement classes. These classes were ported * over to D from the STLSoft (http://stlsoft.org/) C++ libraries, * which were documented in the article "Win32 Performance * Measurement Option", May 2003 issue of Windows Develper Network * (http://www.windevnet.com/documents/win0305a/). * * Created 19th March 2004 * Updated: 18th July 2004 * * www: http://www.digitalmars.com/ * * Copyright (C) 2004 by Digital Mars * All Rights Reserved * Written by Matthew Wilson * www.digitalmars.com * License for redistribution is by either the Artistic License in artistic.txt, * or the LGPL * * ////////////////////////////////////////////////////////////////////////// */ /** \file std/perf.d This file contains platform-independent performance classes */ module std.perf; /// \brief Performance counter scope class generating template /// /// This template declares the class PerformanceCounterScope which manages a /// performance counter instance, scoping its measurement interval by virtue of /// its supporting the RAII mechanism. auto class PerformanceCounterScope(T) { /// \name Construction /// @{ public: /// Constructs an instance of the PerformanceCounterScope using a reference /// to the parameterising performance counter class, whose measurement is /// to be scoped. The measurement interval is commenced with a call to /// start() on the managed counter instance. this(T counter) in { assert(null !is counter); } body { m_counter = counter; m_counter.start(); } /// The measurement interval is completed with a call to stop() on the /// managed counter instance. ~this() { m_counter.stop(); } /// Calls stop() on the managed counter instance, so that intermediate /// timings can be taken. void stop() { m_counter.stop(); } /// Returns a reference to the managed counter instance. T counter() { return m_counter; } /// @} /// \name Members /// @{ private: T m_counter; /// @} // Not to be implemented private: this(PerformanceCounterScope rhs); } version(linux) { extern (C) { private struct timeval { int tv_sec; /*!< The number of seconds, since Jan. 1, 1970, in the time value. */ int tv_usec; /*!< The number of microseconds in the time value. */ }; private struct timezone { int tz_minuteswest; /*!< minutes west of Greenwich. */ int tz_dsttime; /*!< type of dst corrections to apply. */ }; private void gettimeofday(timeval *tv, timezone *tz); } /* ////////////////////////////////////////////////////////////////////////// */ /// \brief A performance counter that uses the most accurate measurement APIs available on the host machine /// /// This class attempts to use the high performance hardware counter as its measurement resource, but failing /// that it defaults to less accurate resources in order to guarantee that meaningful measurements are always /// available to application code /// /// \ingroup group_synsoft_linux_perf class PerformanceCounter { /// \name Types /// @{ private: alias timeval epoch_type; public: /// \brief The interval type /// /// The type of the interval measurement, a 64-bit signed integer alias long interval_type; /// \brief The scope type /// /// The type with which instances of the counter class can be subject to the RAII mechanism in order /// to scope particular measurement intervals alias PerformanceCounterScope!(PerformanceCounter) scope_type; /// @} /// \name Operations /// @{ public: /// \brief Starts measurement /// /// Begins the measurement period void start() { timezone tz; gettimeofday(&m_start, &tz); } /// \brief Ends measurement /// /// Ends the measurement period void stop() { timezone tz; gettimeofday(&m_end, &tz); } /// @} /// \name Attributes /// @{ public: /// \brief The elapsed count in the measurement period /// /// This represents the extent, in machine-specific increments, of the measurement period interval_type periodCount() { return microseconds; } /// \brief The number of whole seconds in the measurement period /// /// This represents the extent, in whole seconds, of the measurement period interval_type seconds() { interval_type start = cast(interval_type)m_start.tv_sec + cast(interval_type)m_start.tv_usec / (1000 * 1000); interval_type end = cast(interval_type)m_end.tv_sec + cast(interval_type)m_end.tv_usec / (1000 * 1000); return end - start; } /// \brief The number of whole milliseconds in the measurement period /// /// This represents the extent, in whole milliseconds, of the measurement period interval_type milliseconds() { interval_type start = cast(interval_type)m_start.tv_sec * 1000 + cast(interval_type)m_start.tv_usec / 1000; interval_type end = cast(interval_type)m_end.tv_sec * 1000 + cast(interval_type)m_end.tv_usec / 1000; return end - start; } /// \brief The number of whole microseconds in the measurement period /// /// This represents the extent, in whole microseconds, of the measurement period interval_type microseconds() { interval_type start = cast(interval_type)m_start.tv_sec * 1000 * 1000 + cast(interval_type)m_start.tv_usec; interval_type end = cast(interval_type)m_end.tv_sec * 1000 * 1000 + cast(interval_type)m_end.tv_usec; return end - start; } /// @} /// \name Members /// @{ private: epoch_type m_start; // start of measurement period epoch_type m_end; // End of measurement period /// @} } unittest { alias PerformanceCounter counter_type; counter_type counter = new counter_type(); counter.start(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us1 = counter.microseconds(); counter_type.interval_type ms1 = counter.milliseconds(); counter_type.interval_type s1 = counter.seconds(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us2 = counter.microseconds(); counter_type.interval_type ms2 = counter.milliseconds(); counter_type.interval_type s2 = counter.seconds(); assert(us2 >= us1); assert(ms2 >= ms1); assert(s2 >= s1); } /* ////////////////////////////////////////////////////////////////////////// */ } else version(Windows) { private import std.c.windows.windows; /* ////////////////////////////////////////////////////////////////////////// */ /// \defgroup group_synsoft_win32_perf synsoft.win32.perf /// \ingroup group_synsoft_win32 /// \brief This library provides Win32 Performance facilities /* ////////////////////////////////////////////////////////////////////////// */ /// \brief A performance counter that uses the high performance hardware counter on the host machine /// /// This class provides high-resolution performance monitoring using the host machine's high performance /// hardware counter. This class does not provide meaningful timing information on operating systems /// that do not provide a high performance hardware counter. /// /// \ingroup group_synsoft_win32_perf class HighPerformanceCounter { /// \name Types /// @{ private: alias long epoch_type; public: /// \brief The interval type /// /// The type of the interval measurement, a 64-bit signed integer alias long interval_type; /// \brief The scope type /// /// The type with which instances of the counter class can be subject to the RAII mechanism in order /// to scope particular measurement intervals alias PerformanceCounterScope!(HighPerformanceCounter) scope_type; /// @} /// \name Construction /// @{ private: static this() { if(!QueryPerformanceFrequency(&sm_freq)) { sm_freq = 0x7fffffffffffffffL; } } /// @} /// \name Operations /// @{ public: /// \brief Starts measurement /// /// Begins the measurement period void start() { QueryPerformanceCounter(&m_start); } /// \brief Ends measurement /// /// Ends the measurement period void stop() { QueryPerformanceCounter(&m_end); } /// @} /// \name Attributes /// @{ public: /// \brief The elapsed count in the measurement period /// /// This represents the extent, in machine-specific increments, of the measurement period interval_type periodCount() { return m_end - m_start; } /// \brief The number of whole seconds in the measurement period /// /// This represents the extent, in whole seconds, of the measurement period interval_type seconds() { return periodCount() / sm_freq; } /// \brief The number of whole milliseconds in the measurement period /// /// This represents the extent, in whole milliseconds, of the measurement period interval_type milliseconds() { interval_type result; interval_type count = periodCount(); if(count < 0x20C49BA5E353F7L) { result = (count * 1000) / sm_freq; } else { result = (count / sm_freq) * 1000; } return result; } /// \brief The number of whole microseconds in the measurement period /// /// This represents the extent, in whole microseconds, of the measurement period interval_type microseconds() { interval_type result; interval_type count = periodCount(); if(count < 0x8637BD05AF6L) { result = (count * 1000000) / sm_freq; } else { result = (count / sm_freq) * 1000000; } return result; } /// @} /// \name Members /// @{ private: epoch_type m_start; // start of measurement period epoch_type m_end; // End of measurement period static interval_type sm_freq; // Frequency /// @} } unittest { alias HighPerformanceCounter counter_type; counter_type counter = new counter_type(); counter.start(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us1 = counter.microseconds(); counter_type.interval_type ms1 = counter.milliseconds(); counter_type.interval_type s1 = counter.seconds(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us2 = counter.microseconds(); counter_type.interval_type ms2 = counter.milliseconds(); counter_type.interval_type s2 = counter.seconds(); assert(us2 >= us1); assert(ms2 >= ms1); assert(s2 >= s1); } /* ////////////////////////////////////////////////////////////////////////// */ /// \brief A low-cost, low-resolution performance counter /// /// This class provides low-resolution, but low-latency, performance monitoring /// and is guaranteed to be meaningful on all operating systems. /// /// \ingroup group_synsoft_win32_perf class TickCounter { /// \name Types /// @{ private: alias long epoch_type; public: /// \brief The interval type /// /// The type of the interval measurement, a 64-bit signed integer alias long interval_type; /// \brief The scope type /// /// The type with which instances of the counter class can be subject to the RAII mechanism in order /// to scope particular measurement intervals alias PerformanceCounterScope!(TickCounter) scope_type; /// @} /// \name Construction /// @{ public: /// @} /// \name Operations /// @{ public: /// \brief Starts measurement /// /// Begins the measurement period void start() { m_start = GetTickCount(); } /// \brief Ends measurement /// /// Ends the measurement period void stop() { m_end = GetTickCount(); } /// @} /// \name Attributes /// @{ public: /// \brief The elapsed count in the measurement period /// /// This represents the extent, in machine-specific increments, of the measurement period interval_type periodCount() { return m_end - m_start; } /// \brief The number of whole seconds in the measurement period /// /// This represents the extent, in whole seconds, of the measurement period interval_type seconds() { return periodCount() / 1000; } /// \brief The number of whole milliseconds in the measurement period /// /// This represents the extent, in whole milliseconds, of the measurement period interval_type milliseconds() { return periodCount(); } /// \brief The number of whole microseconds in the measurement period /// /// This represents the extent, in whole microseconds, of the measurement period interval_type microseconds() { return periodCount() * 1000; } /// @} /// \name Members /// @{ private: uint m_start; // start of measurement period uint m_end; // End of measurement period /// @} } unittest { alias TickCounter counter_type; counter_type counter = new counter_type(); counter.start(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us1 = counter.microseconds(); counter_type.interval_type ms1 = counter.milliseconds(); counter_type.interval_type s1 = counter.seconds(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us2 = counter.microseconds(); counter_type.interval_type ms2 = counter.milliseconds(); counter_type.interval_type s2 = counter.seconds(); assert(us2 >= us1); assert(ms2 >= ms1); assert(s2 >= s1); } /* ////////////////////////////////////////////////////////////////////////// */ /// \brief A performance counter that provides thread-specific performance timings /// /// This class uses the operating system's performance monitoring facilities to provide timing /// information pertaining to the calling thread only, irrespective of the activities of other /// threads on the system. This class does not provide meaningful timing information on operating /// systems that do not provide thread-specific monitoring. /// /// \ingroup group_synsoft_win32_perf class ThreadTimesCounter { /// \name Types /// @{ private: alias long epoch_type; public: /// \brief The interval type /// /// The type of the interval measurement, a 64-bit signed integer alias long interval_type; /// \brief The scope type /// /// The type with which instances of the counter class can be subject to the RAII mechanism in order /// to scope particular measurement intervals alias PerformanceCounterScope!(ThreadTimesCounter) scope_type; /// @} /// \name Construction /// @{ public: /// \brief Constructor /// /// Creates an instance of the class, and caches the thread token so that measurements will /// be taken with respect to the thread in which the class was created. this() { m_thread = GetCurrentThread(); } /// @} /// \name Operations /// @{ public: /// \brief Starts measurement /// /// Begins the measurement period void start() { FILETIME creationTime; FILETIME exitTime; GetThreadTimes(m_thread, &creationTime, &exitTime, cast(FILETIME*)&m_kernelStart, cast(FILETIME*)&m_userStart); } /// \brief Ends measurement /// /// Ends the measurement period void stop() { FILETIME creationTime; FILETIME exitTime; GetThreadTimes(m_thread, &creationTime, &exitTime, cast(FILETIME*)&m_kernelEnd, cast(FILETIME*)&m_userEnd); } /// @} /// \name Attributes /// @{ public: /// \name Kernel /// @{ /// \brief The elapsed count in the measurement period for kernel mode activity /// /// This represents the extent, in machine-specific increments, of the measurement period for kernel mode activity interval_type kernelPeriodCount() { return m_kernelEnd - m_kernelStart; } /// \brief The number of whole seconds in the measurement period for kernel mode activity /// /// This represents the extent, in whole seconds, of the measurement period for kernel mode activity interval_type kernelSeconds() { return kernelPeriodCount() / 10000000; } /// \brief The number of whole milliseconds in the measurement period for kernel mode activity /// /// This represents the extent, in whole milliseconds, of the measurement period for kernel mode activity interval_type kernelMilliseconds() { return kernelPeriodCount() / 10000; } /// \brief The number of whole microseconds in the measurement period for kernel mode activity /// /// This represents the extent, in whole microseconds, of the measurement period for kernel mode activity interval_type kernelMicroseconds() { return kernelPeriodCount() / 10; } /// @} /// \name User /// @{ /// \brief The elapsed count in the measurement period for user mode activity /// /// This represents the extent, in machine-specific increments, of the measurement period for user mode activity interval_type userPeriodCount() { return m_userEnd - m_userStart; } /// \brief The number of whole seconds in the measurement period for user mode activity /// /// This represents the extent, in whole seconds, of the measurement period for user mode activity interval_type userSeconds() { return userPeriodCount() / 10000000; } /// \brief The number of whole milliseconds in the measurement period for user mode activity /// /// This represents the extent, in whole milliseconds, of the measurement period for user mode activity interval_type userMilliseconds() { return userPeriodCount() / 10000; } /// \brief The number of whole microseconds in the measurement period for user mode activity /// /// This represents the extent, in whole microseconds, of the measurement period for user mode activity interval_type userMicroseconds() { return userPeriodCount() / 10; } /// @} /// \name Total /// @{ /// \brief The elapsed count in the measurement period /// /// This represents the extent, in machine-specific increments, of the measurement period interval_type periodCount() { return kernelPeriodCount() + userPeriodCount(); } /// \brief The number of whole seconds in the measurement period /// /// This represents the extent, in whole seconds, of the measurement period interval_type seconds() { return periodCount() / 10000000; } /// \brief The number of whole milliseconds in the measurement period /// /// This represents the extent, in whole milliseconds, of the measurement period interval_type milliseconds() { return periodCount() / 10000; } /// \brief The number of whole microseconds in the measurement period /// /// This represents the extent, in whole microseconds, of the measurement period interval_type microseconds() { return periodCount() / 10; } /// @} /// @} /// \name Members /// @{ private: epoch_type m_kernelStart; epoch_type m_kernelEnd; epoch_type m_userStart; epoch_type m_userEnd; HANDLE m_thread; /// @} } unittest { alias ThreadTimesCounter counter_type; counter_type counter = new counter_type(); counter.start(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us1 = counter.microseconds(); counter_type.interval_type ms1 = counter.milliseconds(); counter_type.interval_type s1 = counter.seconds(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us2 = counter.microseconds(); counter_type.interval_type ms2 = counter.milliseconds(); counter_type.interval_type s2 = counter.seconds(); assert(us2 >= us1); assert(ms2 >= ms1); assert(s2 >= s1); } /* ////////////////////////////////////////////////////////////////////////// */ /// \brief A performance counter that provides process-specific performance timings /// /// This class uses the operating system's performance monitoring facilities to provide timing /// information pertaining to the calling process only, irrespective of the activities of other /// processes on the system. This class does not provide meaningful timing information on operating /// systems that do not provide process-specific monitoring. /// /// \ingroup group_synsoft_win32_perf class ProcessTimesCounter { /// \name Types /// @{ private: alias long epoch_type; public: /// \brief The interval type /// /// The type of the interval measurement, a 64-bit signed integer alias long interval_type; /// \brief The scope type /// /// The type with which instances of the counter class can be subject to the RAII mechanism in order /// to scope particular measurement intervals alias PerformanceCounterScope!(ProcessTimesCounter) scope_type; /// @} /// \name Construction /// @{ private: /// \brief Class constructor /// /// Detects availability of the high performance hardware counter, and if /// not available adjusts static this() { sm_process = GetCurrentProcess(); } /// @} /// \name Operations /// @{ public: /// \brief Starts measurement /// /// Begins the measurement period void start() { FILETIME creationTime; FILETIME exitTime; GetProcessTimes(sm_process, &creationTime, &exitTime, cast(FILETIME*)&m_kernelStart, cast(FILETIME*)&m_userStart); } /// \brief Ends measurement /// /// Ends the measurement period void stop() { FILETIME creationTime; FILETIME exitTime; GetProcessTimes(sm_process, &creationTime, &exitTime, cast(FILETIME*)&m_kernelEnd, cast(FILETIME*)&m_userEnd); } /// @} /// \name Attributes /// @{ public: /// \name Kernel /// @{ /// \brief The elapsed count in the measurement period for kernel mode activity /// /// This represents the extent, in machine-specific increments, of the measurement period for kernel mode activity interval_type kernelPeriodCount() { return m_kernelEnd - m_kernelStart; } /// \brief The number of whole seconds in the measurement period for kernel mode activity /// /// This represents the extent, in whole seconds, of the measurement period for kernel mode activity interval_type kernelSeconds() { return kernelPeriodCount() / 10000000; } /// \brief The number of whole milliseconds in the measurement period for kernel mode activity /// /// This represents the extent, in whole milliseconds, of the measurement period for kernel mode activity interval_type kernelMilliseconds() { return kernelPeriodCount() / 10000; } /// \brief The number of whole microseconds in the measurement period for kernel mode activity /// /// This represents the extent, in whole microseconds, of the measurement period for kernel mode activity interval_type kernelMicroseconds() { return kernelPeriodCount() / 10; } /// @} /// \name User /// @{ /// \brief The elapsed count in the measurement period for user mode activity /// /// This represents the extent, in machine-specific increments, of the measurement period for user mode activity interval_type userPeriodCount() { return m_userEnd - m_userStart; } /// \brief The number of whole seconds in the measurement period for user mode activity /// /// This represents the extent, in whole seconds, of the measurement period for user mode activity interval_type userSeconds() { return userPeriodCount() / 10000000; } /// \brief The number of whole milliseconds in the measurement period for user mode activity /// /// This represents the extent, in whole milliseconds, of the measurement period for user mode activity interval_type userMilliseconds() { return userPeriodCount() / 10000; } /// \brief The number of whole microseconds in the measurement period for user mode activity /// /// This represents the extent, in whole microseconds, of the measurement period for user mode activity interval_type userMicroseconds() { return userPeriodCount() / 10; } /// @} /// \name Total /// @{ /// \brief The elapsed count in the measurement period /// /// This represents the extent, in machine-specific increments, of the measurement period interval_type periodCount() { return kernelPeriodCount() + userPeriodCount(); } /// \brief The number of whole seconds in the measurement period /// /// This represents the extent, in whole seconds, of the measurement period interval_type seconds() { return periodCount() / 10000000; } /// \brief The number of whole milliseconds in the measurement period /// /// This represents the extent, in whole milliseconds, of the measurement period interval_type milliseconds() { return periodCount() / 10000; } /// \brief The number of whole microseconds in the measurement period /// /// This represents the extent, in whole microseconds, of the measurement period interval_type microseconds() { return periodCount() / 10; } /// @} /// @} /// \name Members /// @{ private: epoch_type m_kernelStart; epoch_type m_kernelEnd; epoch_type m_userStart; epoch_type m_userEnd; static HANDLE sm_process; /// @} } unittest { alias ProcessTimesCounter counter_type; counter_type counter = new counter_type(); counter.start(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us1 = counter.microseconds(); counter_type.interval_type ms1 = counter.milliseconds(); counter_type.interval_type s1 = counter.seconds(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us2 = counter.microseconds(); counter_type.interval_type ms2 = counter.milliseconds(); counter_type.interval_type s2 = counter.seconds(); assert(us2 >= us1); assert(ms2 >= ms1); assert(s2 >= s1); } /* ////////////////////////////////////////////////////////////////////////// */ /// \brief A performance counter that uses the most accurate measurement APIs available on the host machine /// /// This class attempts to use the high performance hardware counter as its measurement resource, but failing /// that it defaults to less accurate resources in order to guarantee that meaningful measurements are always /// available to application code /// /// \ingroup group_synsoft_win32_perf class PerformanceCounter { /// \name Types /// @{ private: alias long epoch_type; public: /// \brief The interval type /// /// The type of the interval measurement, a 64-bit signed integer alias long interval_type; /// \brief The scope type /// /// The type with which instances of the counter class can be subject to the RAII mechanism in order /// to scope particular measurement intervals alias PerformanceCounterScope!(PerformanceCounter) scope_type; /// @} /// \name Constructors /// @{ private: /// \brief Class constructor /// /// Detects availability of the high performance hardware counter, and if /// not available adjusts static this() { if(QueryPerformanceFrequency(&sm_freq)) { sm_fn = &_qpc; } else { sm_freq = 1000; sm_fn = &_qtc; } } /// @} /// \name Operations /// @{ public: /// \brief Starts measurement /// /// Begins the measurement period void start() { sm_fn(m_start); } /// \brief Ends measurement /// /// Ends the measurement period void stop() { sm_fn(m_end); } /// @} /// \name Attributes /// @{ public: /// \brief The elapsed count in the measurement period /// /// This represents the extent, in machine-specific increments, of the measurement period interval_type periodCount() { return m_end - m_start; } /// \brief The number of whole seconds in the measurement period /// /// This represents the extent, in whole seconds, of the measurement period interval_type seconds() { return periodCount() / sm_freq; } /// \brief The number of whole milliseconds in the measurement period /// /// This represents the extent, in whole milliseconds, of the measurement period interval_type milliseconds() { interval_type result; interval_type count = periodCount(); if(count < 0x20C49BA5E353F7L) { result = (count * 1000) / sm_freq; } else { result = (count / sm_freq) * 1000; } return result; } /// \brief The number of whole microseconds in the measurement period /// /// This represents the extent, in whole microseconds, of the measurement period interval_type microseconds() { interval_type result; interval_type count = periodCount(); if(count < 0x8637BD05AF6L) { result = (count * 1000000) / sm_freq; } else { result = (count / sm_freq) * 1000000; } return result; } /// @} /// \name Implementation /// @{ private: alias void function(out epoch_type interval) measure_func; static void _qpc(out epoch_type interval) { QueryPerformanceCounter(&interval); } static void _qtc(out epoch_type interval) { interval = GetTickCount(); } /// @} /// \name Members /// @{ private: epoch_type m_start; // start of measurement period epoch_type m_end; // End of measurement period static interval_type sm_freq; // Frequency static measure_func sm_fn; // Measurement function /// @} } unittest { alias PerformanceCounter counter_type; counter_type counter = new counter_type(); counter.start(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us1 = counter.microseconds(); counter_type.interval_type ms1 = counter.milliseconds(); counter_type.interval_type s1 = counter.seconds(); volatile for(int i = 0; i < 10000000; ++i) { } counter.stop(); counter_type.interval_type us2 = counter.microseconds(); counter_type.interval_type ms2 = counter.milliseconds(); counter_type.interval_type s2 = counter.seconds(); assert(us2 >= us1); assert(ms2 >= ms1); assert(s2 >= s1); } /* ////////////////////////////////////////////////////////////////////////// */ } else { const int platform_not_supported = 0; static assert(platform_not_supported); }