// Written in the D programming language

/* /////////////////////////////////////////////////////////////////////////////
 * File:	perf.d
 *
 * 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
 * http://www.digitalmars.com
 * License for redistribution is by either the Artistic License in artistic.txt,
 * or the LGPL
 *
 * ////////////////////////////////////////////////////////////////////////// */


/** 
 * Platform-independent performance measurement and timing classes.
 *
 * $(D_PARAM PerformanceCounter) is the main platform-independent timer class provided, 
 * covering the most typical use case, measuring elapsed wall-clock time.
 *
 * The module also provides several Windows-specific timers that can
 * be useful in specialized situations.
 *
 * Synopsis:
 ----
alias PerformanceCounter.interval_t interval_t;
auto timer = new PerformanceCounter;
timer.start();
// do computation
timer.stop();
interval_t elapsedMsec = timer.milliseconds;
writefln("Time elapsed: %s msec", elapsedMsec);
----
 * In particular note that $(D_PARAM stop()) must be called
 * before querying the elapsed time.
 *
 * These classes were ported to D from the 
 * $(LINK2 http://stlsoft.org/,STLSoft C++ libraries),
 * which were documented in the article 
 * "$(LINK2 http://www.windevnet.com/documents/win0305a/, 
 * Win32 Performance Measurement Options)",
 * May 2003 issue of Windows Develper Network.
 *
 * Author:
 * Matthew Wilson
 *
 * Macros:
 *	WIKI=Phobos/StdPerf
 */

module std.perf;


version(Windows)
{

    private import std.c.windows.windows;

    /* ////////////////////////////////////////////////////////////////////////// */

    // This library provides performance measurement facilities

    /* ////////////////////////////////////////////////////////////////////////// */

    /** A performance counter that uses the most accurate measurement APIs available on the host machine
    
	On Linux, the implementation uses $(D_PARAM gettimeofday()).  
	For Windows, $(D_PARAM QueryPerformanceCounter()) is used if available, 
	$(D_PARAM GetTickCount()) otherwise.
    */
    class PerformanceCounter
    {
    private:
	alias	long	epoch_type;
    public:
	/// The type of the interval measurement (generally a 64-bit signed integer)
	alias	long	interval_t;

        deprecated alias interval_t interval_type;

    private:
	/** Class constructor
	*/
	static this()
	{
	    // Detects availability of the high performance hardware counter, and if
	    // not available adjusts 

	    if(QueryPerformanceFrequency(&sm_freq))
	    {
		sm_fn	=   &_qpc;
	    }
	    else
	    {
		sm_freq =   1000;
		sm_fn	=   &_qtc;
	    }
	}

    public:
	/** Starts measurement
	   
	    Begins a measurement period
	*/ 
	void start()
	{
	    sm_fn(m_start);
	}

	/** Ends measurement
	   
	    Marks the end of a measurement period.
	    This must be called before querying the elapsed time with
	    $(D_PARAM period_count), $(D_PARAM seconds),
	    $(D_PARAM milliseconds), or $(D_PARAM microseconds). 

	    The $(D_PARAM stop()) method may be called multiple times without an intervening $(D_PARAM start()).  
	    Elapsed time is always measured from most recent $(D_PARAM start()) to the most recent 
	    $(D_PARAM stop()).
	*/ 
	void stop()
	{
	    sm_fn(m_end);
	}

    public:
	/** The elapsed count in the measurement period
	   
	    This represents the extent, in machine-specific increments, of the measurement period
	*/ 
	interval_t periodCount() const
	{
	    return m_end - m_start;
	}

	/** The number of whole seconds in the measurement period
	   
	    This represents the extent, in whole seconds, of the measurement period
	*/ 
	interval_t seconds() const
	{
	    return periodCount() / sm_freq;
	}

	/** The number of whole milliseconds in the measurement period
	   
	    This represents the extent, in whole milliseconds, of the measurement period
	*/ 
	interval_t milliseconds() const
	{
	    interval_t   result;
	    interval_t   count   =	periodCount();

	    if(count < 0x20C49BA5E353F7L)
	    {
		result = (count * 1000) / sm_freq;
	    }
	    else
	    {
		result = (count / sm_freq) * 1000;
	    }

	    return result;
	}

	/** The number of whole microseconds in the measurement period
	   
	    This represents the extent, in whole microseconds, of the measurement period
	*/ 
	interval_t microseconds() const
	{
	    interval_t   result;
	    interval_t   count   =	periodCount();

	    if(count < 0x8637BD05AF6L)
	    {
		result = (count * 1000000) / sm_freq;
	    }
	    else
	    {
		result = (count / sm_freq) * 1000000;
	    }

	    return result;
	}

    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();
	}

    private:
	epoch_type		m_start;    // start of measurement period
	epoch_type		m_end;	    // End of measurement period
	static interval_t	sm_freq;    // Frequency
	static measure_func	sm_fn;	    // Measurement function
    }

    unittest
    {
	alias PerformanceCounter    counter_type;

	counter_type	counter = new counter_type();

	counter.start();
	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us1 =   counter.microseconds();
	counter_type.interval_t  ms1 =   counter.milliseconds();
	counter_type.interval_t  s1	=   counter.seconds();

	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us2 =   counter.microseconds();
	counter_type.interval_t  ms2 =   counter.milliseconds();
	counter_type.interval_t  s2	=   counter.seconds();

	assert(us2 >= us1);
	assert(ms2 >= ms1);
	assert(s2 >= s1);
    }

    /* ////////////////////////////////////////////////////////////////////////// */

    /** A low-cost, low-resolution performance counter
       
	This class provides low-resolution, but low-latency, performance monitoring.

	This class is available only on Windows, but 
	is guaranteed to be meaningful on all Windows operating systems.
    */
    class TickCounter
    {
    private:
	alias	long	epoch_type;
    public:
	/** The interval type

	    The type of the interval measurement (generally a 64-bit signed integer)
	*/
	alias	long	interval_t;

        deprecated alias interval_t interval_type;

    public:

    public:
	/** Starts measurement

	    Begins a measurement period
	*/
	void start()
	{
	    m_start = GetTickCount();
	}

	/** Ends measurement
	
	    Marks the end of a measurement period.
	    This must be called before querying the elapsed time with
	    $(D_PARAM period_count), $(D_PARAM seconds),
	    $(D_PARAM milliseconds), or $(D_PARAM microseconds). 

	    The $(D_PARAM stop()) method may be called multiple times without an intervening $(D_PARAM start()).  
	    Elapsed time is always measured from most recent $(D_PARAM start()) to the most recent 
	    $(D_PARAM stop()).
	*/
	void stop()
	{
	    m_end = GetTickCount();
	}

    public:
	/**
	   The elapsed count in the measurement period
	
	   This represents the extent, in machine-specific increments, of the measurement period
	*/
	interval_t periodCount() const
	{
	    return m_end - m_start;
	}

	/** The number of whole seconds in the measurement period

	    This represents the extent, in whole seconds, of the measurement period
	*/
	interval_t seconds() const
	{
	    return periodCount() / 1000;
	}

	/** The number of whole milliseconds in the measurement period

	    This represents the extent, in whole milliseconds, of the measurement period
	*/
	interval_t milliseconds() const
	{
	    return periodCount();
	}

	/** The number of whole microseconds in the measurement period

	    This represents the extent, in whole microseconds, of the measurement period
	*/
	interval_t microseconds() const
	{
	    return periodCount() * 1000;
	}

    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();
	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us1 =   counter.microseconds();
	counter_type.interval_t  ms1 =   counter.milliseconds();
	counter_type.interval_t  s1	=   counter.seconds();

	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us2 =   counter.microseconds();
	counter_type.interval_t  ms2 =   counter.milliseconds();
	counter_type.interval_t  s2	=   counter.seconds();

	assert(us2 >= us1);
	assert(ms2 >= ms1);
	assert(s2 >= s1);
    }

    /* ////////////////////////////////////////////////////////////////////////// */

    /** 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.

	This class is available only on Windows.
    */
    class ThreadTimesCounter
    {
    private:
	alias	long	epoch_type;
    public:
	/** The interval type
	   
	    The type of the interval measurement (generally a 64-bit signed integer)
	*/ 
	alias	long	interval_t;

        deprecated alias interval_t interval_type;

    public:
	/** 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();
	}

    public:
	/** Starts measurement
	   
	    Begins a measurement period
	*/
	void start()
	{
	    FILETIME	creationTime;
	    FILETIME	exitTime;

	    GetThreadTimes(m_thread, &creationTime, &exitTime, cast(FILETIME*)&m_kernelStart, cast(FILETIME*)&m_userStart);
	}

	/** Ends measurement
	   
	    Marks the end of a measurement period.
	    This must be called before querying the elapsed time with
	    $(D_PARAM period_count), $(D_PARAM seconds),
	    $(D_PARAM milliseconds), or $(D_PARAM microseconds). 

	    The $(D_PARAM stop()) method may be called multiple times without an intervening $(D_PARAM start()).  
	    Elapsed time is always measured from most recent $(D_PARAM start()) to the most recent 
	    $(D_PARAM stop()).
	*/
	void stop()
	{
	    FILETIME	creationTime;
	    FILETIME	exitTime;

	    GetThreadTimes(m_thread, &creationTime, &exitTime, cast(FILETIME*)&m_kernelEnd, cast(FILETIME*)&m_userEnd);
	}


    public:

	/** 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_t kernelPeriodCount() const
	{
	    return m_kernelEnd - m_kernelStart;
	}
	/** 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_t kernelSeconds() const
	{
	    return kernelPeriodCount() / 10000000;
	}
	/** 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_t kernelMilliseconds() const
	{
	    return kernelPeriodCount() / 10000;
	}
	/** 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_t kernelMicroseconds() const
	{
	    return kernelPeriodCount() / 10;
	}


	/** 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_t userPeriodCount() const
	{
	    return m_userEnd - m_userStart;
	}
	/** 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_t userSeconds() const
	{
	    return userPeriodCount() / 10000000;
	}
	/** 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_t userMilliseconds() const
	{
	    return userPeriodCount() / 10000;
	}
	/** 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_t userMicroseconds() const
	{
	    return userPeriodCount() / 10;
	}


	/** The elapsed count in the measurement period
	   
	    This represents the extent, in machine-specific increments, of the measurement period
	*/ 
	interval_t periodCount() const
	{
	    return kernelPeriodCount() + userPeriodCount();
	}

	/** The number of whole seconds in the measurement period
	   
	    This represents the extent, in whole seconds, of the measurement period
	*/ 
	interval_t seconds() const
	{
	    return periodCount() / 10000000;
	}

	/** The number of whole milliseconds in the measurement period
	   
	    This represents the extent, in whole milliseconds, of the measurement period
	*/ 
	interval_t milliseconds() const
	{
	    return periodCount() / 10000;
	}

	/** The number of whole microseconds in the measurement period
	   
	    This represents the extent, in whole microseconds, of the measurement period
	*/ 
	interval_t microseconds() const
	{
	    return periodCount() / 10;
	}


    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();
	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us1 =   counter.microseconds();
	counter_type.interval_t  ms1 =   counter.milliseconds();
	counter_type.interval_t  s1	=   counter.seconds();

	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us2 =   counter.microseconds();
	counter_type.interval_t  ms2 =   counter.milliseconds();
	counter_type.interval_t  s2	=   counter.seconds();

	assert(us2 >= us1);
	assert(ms2 >= ms1);
	assert(s2 >= s1);
    }

    /* ////////////////////////////////////////////////////////////////////////// */

    /** 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.

	This class is available only on Windows.
    */
    class ProcessTimesCounter
    {
    private:
	alias	long	epoch_type;
    public:
	/** The interval type
	   
	    The type of the interval measurement (generally a 64-bit signed integer)
	*/ 
	alias	long	interval_t;

        deprecated alias interval_t interval_type;

    private:
	/** Class constructor
	   
	*/ 
	static this()
	{
	    sm_process = GetCurrentProcess();
	}

    public:
	/** Starts measurement
	   
	    Begins a measurement period
	*/
	void start()
	{
	    FILETIME	creationTime;
	    FILETIME	exitTime;

	    GetProcessTimes(sm_process, &creationTime, &exitTime, cast(FILETIME*)&m_kernelStart, cast(FILETIME*)&m_userStart);
	}

	/** Ends measurement
	   
	    Marks the end of a measurement period.
	    This must be called before querying the elapsed time with
	    $(D_PARAM period_count), $(D_PARAM seconds),
	    $(D_PARAM milliseconds), or $(D_PARAM microseconds). 

	    The $(D_PARAM stop()) method may be called multiple times without an intervening $(D_PARAM start()).  
	    Elapsed time is always measured from most recent $(D_PARAM start()) to the most recent 
	    $(D_PARAM stop()).
	*/
	void stop()
	{
	    FILETIME	creationTime;
	    FILETIME	exitTime;

	    GetProcessTimes(sm_process, &creationTime, &exitTime, cast(FILETIME*)&m_kernelEnd, cast(FILETIME*)&m_userEnd);
	}

    public:
	/** 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_t kernelPeriodCount() const
	{
	    return m_kernelEnd - m_kernelStart;
	}
	/** 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_t kernelSeconds() const
	{
	    return kernelPeriodCount() / 10000000;
	}
	/** 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_t kernelMilliseconds() const
	{
	    return kernelPeriodCount() / 10000;
	}
	/** 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_t kernelMicroseconds() const
	{
	    return kernelPeriodCount() / 10;
	}


	/** 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_t userPeriodCount() const
	{
	    return m_userEnd - m_userStart;
	}
	/** 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_t userSeconds() const
	{
	    return userPeriodCount() / 10000000;
	}
	/** 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_t userMilliseconds() const
	{
	    return userPeriodCount() / 10000;
	}
	/** 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_t userMicroseconds() const
	{
	    return userPeriodCount() / 10;
	}

	/** The elapsed count in the measurement period
	   
	    This represents the extent, in machine-specific increments, of the measurement period
	*/ 
	interval_t periodCount() const
	{
	    return kernelPeriodCount() + userPeriodCount();
	}

	/** The number of whole seconds in the measurement period
	   
	    This represents the extent, in whole seconds, of the measurement period
	*/ 
	interval_t seconds() const
	{
	    return periodCount() / 10000000;
	}

	/** The number of whole milliseconds in the measurement period
	   
	    This represents the extent, in whole milliseconds, of the measurement period
	*/ 
	interval_t milliseconds() const
	{
	    return periodCount() / 10000;
	}

	/** The number of whole microseconds in the measurement period
	   
	    This represents the extent, in whole microseconds, of the measurement period
	*/ 
	interval_t microseconds() const
	{
	    return periodCount() / 10;
	}

    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();
	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us1 =   counter.microseconds();
	counter_type.interval_t  ms1 =   counter.milliseconds();
	counter_type.interval_t  s1	=   counter.seconds();

	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us2 =   counter.microseconds();
	counter_type.interval_t  ms2 =   counter.milliseconds();
	counter_type.interval_t  s2	=   counter.seconds();

	assert(us2 >= us1);
	assert(ms2 >= ms1);
	assert(s2 >= s1);
    }

    /* ////////////////////////////////////////////////////////////////////////// */
}
else version(Posix)
{
    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);
    }

    /* ////////////////////////////////////////////////////////////////////////// */

    class PerformanceCounter
    {
	// documentation is in the Windows version of the class above


    private:
	alias	timeval epoch_type;
    public:
	alias	long	interval_t;

    public:
	void start()
	{
	    timezone tz;

	    gettimeofday(&m_start, &tz);
	}

	void stop()
	{
	    timezone tz;

	    gettimeofday(&m_end, &tz);
	}

    public:
	interval_t periodCount() const
	{
	    return microseconds;
	}

	interval_t seconds() const
	{
	    interval_t   start   =	cast(interval_t)m_start.tv_sec + cast(interval_t)m_start.tv_usec / (1000 * 1000);
	    interval_t   end	    =	cast(interval_t)m_end.tv_sec	  + cast(interval_t)m_end.tv_usec   / (1000 * 1000);

	    return end - start;
	}

	interval_t milliseconds() const
	{
	    interval_t   start   =	cast(interval_t)m_start.tv_sec * 1000 + cast(interval_t)m_start.tv_usec / 1000;
	    interval_t   end	    =	cast(interval_t)m_end.tv_sec	  * 1000 + cast(interval_t)m_end.tv_usec   / 1000;

	    return end - start;
	}

	interval_t microseconds() const
	{
	    interval_t   start   =	cast(interval_t)m_start.tv_sec * 1000 * 1000 + cast(interval_t)m_start.tv_usec;
	    interval_t   end	    =	cast(interval_t)m_end.tv_sec	  * 1000 * 1000 + cast(interval_t)m_end.tv_usec;

	    return end - start;
	}

    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();
	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us1 =   counter.microseconds();
	counter_type.interval_t  ms1 =   counter.milliseconds();
	counter_type.interval_t  s1	=   counter.seconds();

	for(int i = 0; i < 10000000; ++i)
	{   }
	counter.stop();

	counter_type.interval_t  us2 =   counter.microseconds();
	counter_type.interval_t  ms2 =   counter.milliseconds();
	counter_type.interval_t  s2	=   counter.seconds();

	assert(us2 >= us1);
	assert(ms2 >= ms1);
	assert(s2 >= s1);
    }

    /* ////////////////////////////////////////////////////////////////////////// */
}
else
{
    const int platform_not_supported = 0;

    static assert(platform_not_supported);
}
