phobos/std/datetime.d
SHOO 7cd2e0339e std.datetime: Added benchmark
This function came from std.date.
But now, benchmark returns Ticks[N] and don't use result argument.
I removed need to use GC for by doing it in this way.
2010-09-18 11:04:14 +00:00

1002 lines
23 KiB
D

/*******************************************************************************
* Platform-independent high precision StopWatch.
*
* This module provide:
* $(UL
* $(LI StopWatch)
* $(LI Benchmarks)
* $(LI Some helper functions)
* )
*
*
* License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Kato Shoichi
*/
module std.datetime;
@safe:
import std.traits, std.exception, std.functional;
version (Windows)
{
import core.sys.windows.windows;
import std.windows.syserror;
}
else version (Posix)
{
import core.sys.posix.time;
import core.sys.posix.sys.time;
}
//##############################################################################
//##############################################################################
//###
//### StopWatch
//###
//##############################################################################
//##############################################################################
/*******************************************************************************
* System clock time.
*
* This type maintains the most high precision ticks of system clock in each
* environment.
* (For StopWatch)
*/
struct Ticks
{
@safe:// @@@BUG@@@ workaround for bug 4211
/***************************************************************************
* Ticks that is counted per 1[s].
*
* Confirm that it is not 0, to examine whether you can use Ticks.
*/
static immutable long ticksPerSec;
/***************************************************************************
* Ticks when application begins.
*/
static immutable Ticks appOrigin;
@trusted
shared static this()
{
version (Windows)
{
if (QueryPerformanceFrequency(cast(long*)&ticksPerSec) == 0)
{
ticksPerSec = 0;
}
}
else version (Posix)
{
static if (is(typeof({ auto fp = &clock_gettime; })))
{
timespec ts;
if ( clock_getres(CLOCK_REALTIME, &ts) != 0)
{
ticksPerSec = 0;
}
else
{
ticksPerSec = 1000_000_000 / ts.tv_nsec;
}
}
else
{
ticksPerSec = 1_000_000;
}
}
if (ticksPerSec != 0)
{
appOrigin = systime();
}
}
unittest
{
assert(ticksPerSec);
}
/***************************************************************************
* Unknown value for Ticks
*
* You can convert this value into number of seconds by dividing it
* by ticksPerSec.
*/
long value;
/***************************************************************************
* [s] as integer or real number
*
* Attention: This method truncate the number of digits after decimal point.
*/
const
T toSeconds(T)() if (isIntegral!T && T.sizeof >= 4)
{
return cast(T)(value/ticksPerSec);
}
/// ditto
const
T toSeconds(T)() if (isFloatingPoint!T)
{
//@@@BUG@@@ workaround for bug 4689
long t = ticksPerSec;
return value/cast(T)t;
}
/***************************************************************************
* [s] as real number
*/
@property alias toSeconds!real seconds;
/***************************************************************************
* [s] as integer
*/
@property alias toSeconds!long sec;
unittest
{
auto t = Ticks(ticksPerSec);
assert(t.sec == 1);
t = Ticks(ticksPerSec-1);
assert(t.sec == 0);
t = Ticks(ticksPerSec*2);
assert(t.sec == 2);
t = Ticks(ticksPerSec*2-1);
assert(t.sec == 1);
t = Ticks(-1);
assert(t.sec == 0);
t = Ticks(-ticksPerSec-1);
assert(t.sec == -1);
t = Ticks(-ticksPerSec);
assert(t.sec == -1);
}
/***************************************************************************
* Create Ticks from [s] as integer
*/
static Ticks fromSeconds(T)(T sec) if (isNumeric!T)
{
return Ticks(cast(long)(sec * ticksPerSec));
}
unittest
{
auto t = Ticks.fromSeconds(1000000);
assert(t.sec == 1000000);
t = Ticks.fromSeconds(2000000);
assert(t.sec == 2000000);
t.value -= 1;
assert(t.sec == 1999999);
}
/***************************************************************************
* [ms] as integer or real number
*/
const
T toMilliseconds(T)() if (isIntegral!T && T.sizeof >= 4)
{
return value/(ticksPerSec/1000);
}
/// ditto
const
T toMilliseconds(T)() if (isFloatingPoint!T)
{
return toSeconds!T * 1000;
}
/***************************************************************************
* [ms] as real number
*/
@property alias toMilliseconds!real milliseconds;
/***************************************************************************
* [ms] as integer
*/
@property alias toMilliseconds!long msec;
/***************************************************************************
* Create Ticks from [ms] as integer
*/
static Ticks fromMilliseconds(long msec)
{
return Ticks(msec*(ticksPerSec/1000));
}
unittest
{
auto t = Ticks.fromMilliseconds(1000000);
assert(t.msec == 1000000);
t = Ticks.fromMilliseconds(2000000);
assert(t.msec == 2000000);
t.value -= 1;
assert(t.msec == 1999999);
}
/***************************************************************************
* [us] as integer or real number
*/
const
T toMicroseconds(T)() if (isIntegral!T && T.sizeof >= 4)
{
return value/(ticksPerSec/1000/1000);
}
/// ditto
const
T toMicroseconds(T)() if (isFloatingPoint!T)
{
return toMilliseconds!T * 1000;
}
/***************************************************************************
* [us] as real number
*/
@property alias toMicroseconds!real microseconds;
/***************************************************************************
* [us] as integer
*/
alias toMicroseconds!long usec;
/***************************************************************************
* Create Ticks from [us] as integer
*/
static Ticks fromMicroseconds(long usec)
{
return Ticks(usec*(ticksPerSec/1000/1000));
}
unittest
{
auto t = Ticks.fromMicroseconds(1000000);
assert(t.usec == 1000000);
t = Ticks.fromMicroseconds(2000000);
assert(t.usec == 2000000);
t.value -= 1;
assert(t.usec == 1999999);
}
/***************************************************************************
* operator overroading "-=, +="
*
* BUG: This should be return "ref Ticks", but bug2460 prevents that.
*/
void opOpAssign(string op)(in Ticks t) if (op == "+" || op == "-")
{
mixin("value "~op~"= t.value;");
//return this;
}
unittest
{
Ticks a = systime(), b = systime();
a += systime();
assert(a.seconds >= 0);
b -= systime();
assert(b.seconds <= 0);
}
/***************************************************************************
* operator overroading "-, +"
*/
const
Ticks opBinary(string op)(in Ticks t) if (op == "-" || op == "+")
{
Ticks lhs = this;
lhs.opOpAssign!op(t);
return lhs;
}
unittest
{
auto a = systime();
auto b = systime();
assert((a + b).seconds > 0);
assert((a - b).seconds <= 0);
}
/***************************************************************************
* operator overroading "=="
*/
const
equals_t opEquals(ref const Ticks t)
{
return value == t.value;
}
unittest
{
auto t1 = systime();
assert(t1 == t1);
}
/***************************************************************************
* operator overroading "<, >, <=, >="
*/
const
int opCmp(ref const Ticks t)
{
return value < t.value? -1: value == t.value ? 0 : 1;
}
unittest
{
auto t1 = systime();
auto t2 = systime();
assert(t1 <= t2);
assert(t2 >= t1);
}
/***************************************************************************
* operator overroading "*=, /="
*/
void opOpAssign(string op, T)(T x)
if ((op == "*" || op == "/") && isNumeric!(T))
{
mixin("value "~op~"= x;");
}
unittest
{
immutable t = systime();
// *
{
Ticks t1 = t, t2 = t;
t1 *= 2;
assert(t < t1);
t2 *= 2.1L;
assert(t2 > t1);
}
// /
{
Ticks t1 = t, t2 = t;
t1 /= 2;
assert(t1 < t);
t2 /= 2.1L;
assert(t2 < t1);
}
}
/***************************************************************************
* operator overroading "*", "/"
*/
Ticks opBinary(string op, T)(T x)
if ((op == "*" || op == "/") && isNumeric!(T))
{
auto lhs = this;
lhs.opOpAssign!op(x);
return lhs;
}
unittest
{
auto t = systime();
auto t2 = t*2;
assert(t < t2);
assert(t*3.5 > t2);
}
/***************************************************************************
* operator overroading "/"
*/
const
real opBinary(string op)(Ticks x) if (op == "/")
{
return value / cast(real)x.value;
}
unittest
{
auto t = systime();
assert(t/systime() <= 1);
}
}
/***************************************************************************
* Special type for constructor
*/
enum AutoStart
{
///
no,
///
yes
}
/*******************************************************************************
* StopWatch's AutoStart flag
*/
enum autoStart = AutoStart.yes;
/*******************************************************************************
* StopWatch measures time highly precise as possible.
*
* This class uses performance counter.
* On Windows, This uses QueryPerformanceCounter.
* For Posix, This uses clock_gettime if available, gettimeofday otherwise.
*
* But this has dispersion in accuracy by environment.
* It is impossible to remove this dispersion. This depends on multi task
* system for example overhead from change of the context switch of the thread.
*
* Usage is here:
* Example:
*------------------------------------------------------------------------------
*void foo() {
* StopWatch sw;
* static immutable N = 100;
* Ticks[N] times;
* Ticks last = Ticks.fromSeconds(0);
* foreach (i; 0..N) {
* sw.start(); // start/resume mesuring.
* foreach (Unused; 0..1000000) bar();
* sw.stop(); // stop/pause mesuring.
* // Return value of peek() after having stopped are the always same.
* writeln((i+1)*1000000, " times done, lap time: ",
* sw.peek().msec, "[ms]");
* times[i] = sw.peek() - last;
* last = sw.peek();
* }
* real sum = 0;
* // When you want to know the number of seconds of the fact,
* // you can use properties of Ticks.
* // (seconds, mseconds, useconds, interval)
* foreach (e; times) sum += e.interval;
* writeln("Average time: ", sum/N, "[s]");
*}
*------------------------------------------------------------------------------
*/
struct StopWatch
{
@safe:// @@@BUG@@@ workaround for bug 4211
private:
// true if observing.
bool m_FlagStarted = false;
// Ticks at the time of StopWatch starting mesurement.
Ticks m_TimeStart;
// Ticks as total time of measurement.
Ticks m_TimeMeasured;
public:
/***************************************************************************
* auto start with constructor
*/
this(AutoStart autostart)
{
if (autostart)
{
start();
}
}
unittest
{
auto sw = StopWatch(autoStart);
sw.stop();
}
/***************************************************************************
* Reset the time measurement.
*/
@safe
void reset()
{
if (m_FlagStarted)
{
// Set current systime if StopWatch is measuring.
m_TimeStart = systime();
}
else
{
// Set zero if StopWatch is not measuring.
m_TimeStart.value = 0;
}
m_TimeMeasured.value = 0;
}
unittest
{
StopWatch sw;
sw.start();
sw.stop();
sw.reset();
assert(sw.peek().seconds == 0);
}
/***************************************************************************
* Start the time measurement.
*/
@safe
void start()
{
assert(!m_FlagStarted);
StopWatch sw;
m_FlagStarted = true;
m_TimeStart = systime();
}
unittest
{
StopWatch sw;
sw.start();
auto t1 = sw.peek();
bool doublestart = true;
try sw.start();
catch (Error e) doublestart = false;
assert(!doublestart);
sw.stop();
assert((t1 - sw.peek()).seconds <= 0);
}
/***************************************************************************
* Stop the time measurement.
*/
@safe
void stop()
{
assert(m_FlagStarted);
m_FlagStarted = false;
m_TimeMeasured += systime() - m_TimeStart;
}
unittest
{
StopWatch sw;
sw.start();
sw.stop();
auto t1 = sw.peek();
bool doublestop = true;
try sw.stop();
catch (Error e) doublestop = false;
assert(!doublestop);
assert((t1 - sw.peek()).seconds == 0);
}
/***************************************************************************
* Peek Ticks of measured time.
*/
@safe const
Ticks peek()
{
if(m_FlagStarted)
{
return systime() - m_TimeStart + m_TimeMeasured;
}
return m_TimeMeasured;
}
unittest
{
StopWatch sw;
sw.start();
auto t1 = sw.peek();
sw.stop();
auto t2 = sw.peek();
auto t3 = sw.peek();
assert(t1 <= t2);
assert(t2 == t3);
}
}
/*******************************************************************************
* Ticks of system time.
*/
@trusted
Ticks systime()
{
version (Windows)
{
ulong ticks;
if (QueryPerformanceCounter(cast(long*)&ticks) == 0)
{
throw new Exception(sysErrorString(GetLastError()));
}
return Ticks(ticks);
}
else version (Posix)
{
static if (is(typeof({auto f = &clock_gettime;})))
{
timespec ts;
errnoEnforce(clock_gettime(CLOCK_REALTIME, &ts) == 0,
"Failed in gettimeofday");
return Ticks(ts.tv_sec * Ticks.ticksPerSec +
ts.tv_nsec * Ticks.ticksPerSec / 1000 / 1000 / 1000);
}
else
{
timeval tv;
errnoEnforce(gettimeofday(&tv, null) == 0,
"Failed in gettimeofday");
return Ticks(tv.tv_sec * Ticks.ticksPerSec +
tv.tv_usec * Ticks.ticksPerSec / 1000 / 1000);
}
}
}
unittest
{
auto t = systime();
assert(t.value);
}
/*******************************************************************************
* Ticks when application begin running.
*/
@safe
Ticks apptime()
{
return systime() - Ticks.appOrigin;
}
unittest
{
auto a = systime();
auto b = apptime();
assert(a.value);
assert(a != b);
}
//##############################################################################
//##############################################################################
//###
//### Benchmarks
//###
//##############################################################################
//##############################################################################
// workaround for bug4886
@safe pure nothrow
size_t lengthof(aliases...)()
{
return aliases.length;
}
/*******************************************************************************
* Benchmarks code for speed assessment and comparison.
*
* Params:
*
* fun = aliases of callable objects (e.g. function names). Each should
* take no arguments.
*
* times = The number of times each function is to be executed.
*
* Returns:
*
* An array of $(D n) $(D uint)s. Element at slot $(D i) contains the
* number of milliseconds spent in calling the $(D i)th function $(D
* times) times.
*
* Examples:
*------------------------------------------------------------------------------
*int a;
*void f0() { }
*void f1() { auto b = a; }
*void f2() { auto b = to!(string)(a); }
*auto r = benchmark!(f0, f1, f2)(10_000_000);
*------------------------------------------------------------------------------
*/
@safe
Ticks[lengthof!(fun)()] benchmark(fun...)(uint times)
if(areAllSafe!fun)
{
Ticks[lengthof!(fun)()] result;
StopWatch sw;
sw.start();
foreach (i, Unused; fun)
{
sw.reset();
foreach (j; 0 .. times)
{
fun[i]();
}
result[i] = sw.peek();
}
return result;
}
@system
Ticks[lengthof!(fun)()] benchmark(fun...)(uint times)
if(!areAllSafe!fun)
{
Ticks[lengthof!(fun)()] result;
StopWatch sw;
sw.start();
foreach (i, Unused; fun)
{
sw.reset();
foreach (j; 0 .. times)
{
fun[i]();
}
result[i] = sw.peek();
}
return result;
}
unittest
{
int a;
void f0() { }
//void f1() { auto b = to!(string)(a); }
void f2() { auto b = (a); }
auto r = benchmark!(f0, f2)(100);
}
/*******************************************************************************
* Return value of benchmark with two functions comparing.
*/
immutable struct ComparingBenchmarkResult
{
@safe:
private Ticks m_tmBase;
private Ticks m_tmTarget;
/***************************************************************************
* Evaluation value
*
* This return the evaluation value of performance as the ratio that is
* compared between BaseFunc's time and TargetFunc's time.
* If performance is high, this returns a high value.
*/
@property immutable
real point()
{
// @@@BUG@@@ workaround for bug 4689
long t = m_tmTarget.value;
return m_tmBase.value / cast(real)t;
}
/***************************************************************************
* The time required of the target function
*/
@property immutable
public Ticks targetTime()
{
return m_tmTarget;
}
/***************************************************************************
* The time required of the base function
*/
@property immutable
public Ticks baseTime()
{
return m_tmBase;
}
}
/*******************************************************************************
* Benchmark with two functions comparing.
*
* Params:
* baseFunc = The function to become the base of the speed.
* targetFunc = The function that wants to measure speed.
* times = The number of times each function is to be executed.
* Examples:
*------------------------------------------------------------------------------
*void f1() {
* // ...
*}
*void f2() {
* // ...
*}
*
*void main() {
* auto b = comparingBenchmark!(f1, f2, 0x80);
* writeln(b.point);
*}
*------------------------------------------------------------------------------
*/
@safe
ComparingBenchmarkResult comparingBenchmark(
alias baseFunc, alias targetFunc, int times = 0xfff)()
if (isSafe!baseFunc && isSafe!targetFunc)
{
auto t = benchmark!(baseFunc, targetFunc)(times);
return ComparingBenchmarkResult(t[0], t[1]);
}
/// ditto
@system
ComparingBenchmarkResult comparingBenchmark(
alias baseFunc, alias targetFunc, int times = 0xfff)()
if (!(isSafe!baseFunc && isSafe!targetFunc))
{
auto t = benchmark!(baseFunc, targetFunc)(times);
return ComparingBenchmarkResult(t[0], t[1]);
}
@safe
unittest
{
@system void f1x() { }
@system void f2x() { }
@safe void f1o() { }
@safe void f2o() { }
auto b1 = comparingBenchmark!(f1o, f2o, 1); // OK
//static auto b2 = comparingBenchmark!(f1x, f2x, 1); // NG
}
@system
unittest
{
@system void f1x() { }
@system void f2x() { }
@safe void f1o() { }
@safe void f2o() { }
auto b1 = comparingBenchmark!(f1o, f2o, 1); // OK
auto b2 = comparingBenchmark!(f1x, f2x, 1); // OK
}
//##############################################################################
//##############################################################################
//###
//### Helper functions
//###
//##############################################################################
//##############################################################################
version (D_Ddoc)
{
/***************************************************************************
* Scope base measuring time.
*
* When a value that is returned by this function is destroyed,
* func will run.
* func is unaly function that requires Ticks.
*
* Examples:
*--------------------------------------------------------------------------
*writeln("benchmark start!");
*{
* auto mt = measureTime!((a){assert(a.seconds);});
* doSomething();
*}
*writeln("benchmark end!");
*--------------------------------------------------------------------------
*/
TemporaryValue measureTime(alias func)();
}
else
{
@safe
{
auto measureTime(alias func)()
if (isSafe!func)
{
struct TMP
{
private StopWatch sw = void;
this(StopWatch.AutoStart as)
{
sw = StopWatch(as);
}
~this()
{
unaryFun!(func)(sw.peek());
}
}
return TMP(autoStart);
}
}
@system
{
auto measureTime(alias func)()
if (!isSafe!func)
{
struct TMP
{
private StopWatch sw = void;
this(AutoStart as)
{
sw = StopWatch(as);
}
~this()
{
unaryFun!(func)(sw.peek());
}
}
return TMP(autoStart);
}
}
}
@system
unittest
{
{
auto mt = measureTime!((a){assert(a.seconds <>= 0);});
}
/+
with (measureTime!((a){assert(a.seconds);}))
{
// doSomething();
// @@@BUG@@@ doesn't work yet.
}
+/
}