diff --git a/vlib/benchmark/benchmark.v b/vlib/benchmark/benchmark.v index 82c551cb29..861ea59e42 100644 --- a/vlib/benchmark/benchmark.v +++ b/vlib/benchmark/benchmark.v @@ -54,8 +54,8 @@ const ( pub struct Benchmark { pub mut: - bench_timer time.Timer - step_timer time.Timer + bench_timer time.StopWatch + step_timer time.StopWatch ntotal int nok int nfail int @@ -70,14 +70,14 @@ pub mut: pub fn new_benchmark() Benchmark { return Benchmark{ - bench_timer: time.new_timer() + bench_timer: time.new_stopwatch() verbose: true } } pub fn new_benchmark_no_cstep() Benchmark { return Benchmark{ - bench_timer: time.new_timer() + bench_timer: time.new_stopwatch() verbose: true no_cstep: true } @@ -85,7 +85,7 @@ pub fn new_benchmark_no_cstep() Benchmark { pub fn new_benchmark_pointer() &Benchmark { return &Benchmark{ - bench_timer: time.new_timer() + bench_timer: time.new_stopwatch() verbose: true } } @@ -147,7 +147,7 @@ pub fn start() Benchmark { pub fn (b mut Benchmark) measure(label string) i64 { b.ok() - res := b.step_timer.elapsed() + res := b.step_timer.elapsed().milliseconds() println(b.step_message_with_label(BSPENT, 'in $label')) b.step() return res @@ -172,10 +172,10 @@ pub fn (b &Benchmark) step_message_with_label(label string, msg string) string { '${b.cstep:3d}/${b.nexpected_steps:3d}' } } - timed_line = b.tdiff_in_ms('[${sprogress}] $msg', b.step_timer.elapsed()) + timed_line = b.tdiff_in_ms('[${sprogress}] $msg', b.step_timer.elapsed().milliseconds()) } else { - timed_line = b.tdiff_in_ms(msg, b.step_timer.elapsed()) + timed_line = b.tdiff_in_ms(msg, b.step_timer.elapsed().milliseconds()) } return '${label:-5s}${timed_line}' } @@ -201,11 +201,11 @@ pub fn (b &Benchmark) total_message(msg string) string { if b.verbose { tmsg = '<=== total time spent $tmsg' } - return ' ' + b.tdiff_in_ms(tmsg, b.bench_timer.elapsed()) + return ' ' + b.tdiff_in_ms(tmsg, b.bench_timer.elapsed().milliseconds()) } pub fn (b &Benchmark) total_duration() i64 { - return b.bench_timer.elapsed() + return b.bench_timer.elapsed().milliseconds() } // ////////////////////////////////////////////////////////////////// diff --git a/vlib/time/stopwatch.v b/vlib/time/stopwatch.v new file mode 100644 index 0000000000..d93b4d2785 --- /dev/null +++ b/vlib/time/stopwatch.v @@ -0,0 +1,52 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module time + +pub struct StopWatch { + pause_time u64 +pub: + start u64 + end u64 +} + +pub fn new_stopwatch() StopWatch { + return StopWatch{start: time.sys_mono_now()} +} + +// start Starts the timer. If the timer was paused, restarts counting. +pub fn (t mut StopWatch) start() { + if t.pause_time == 0 { + t.start = time.sys_mono_now() + } else { + t.start += time.sys_mono_now() - t.pause_time + } + t.end = 0 + t.pause_time = 0 +} + +pub fn (t mut StopWatch) restart() { + t.end = 0 + t.pause_time = 0 + t.start = time.sys_mono_now() +} + +pub fn (t mut StopWatch) stop() { + t.end = time.sys_mono_now() + t.pause_time = 0 +} + +pub fn (t mut StopWatch) pause() { + t.pause_time = time.sys_mono_now() + t.end = t.pause_time // so elapsed keeps track of actual running time +} + +// elapsed returns the Duration since the last start call +pub fn (t StopWatch) elapsed() Duration { + if t.end == 0 { + return Duration(time.sys_mono_now() - t.start) + } else { + return Duration(t.end - t.start) + } +} + diff --git a/vlib/time/time.v b/vlib/time/time.v index 82e9a3eaa2..0f6328e60b 100644 --- a/vlib/time/time.v +++ b/vlib/time/time.v @@ -20,7 +20,21 @@ const ( days_per_400_years = 365 * 400 + 97 days_per_100_years = 365 * 100 + 24 days_per_4_years = 365 * 4 + 1 - days_before = [0, 31, 31 + 28, 31 + 28 + 31, 31 + 28 + 31 + 30, 31 + 28 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, ] + days_before = [ + 0, + 31, + 31 + 28, + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, + ] ) pub struct Time { @@ -121,7 +135,7 @@ pub fn (t Time) unix_time() int { return make_unix_time(tt) } -// add_days returns a new time struct with an added number of seconds. +// add_seconds returns a new time struct with an added number of seconds. pub fn (t Time) add_seconds(seconds int) Time { // TODO Add(d time.Duration) return unix(t.unix + seconds) @@ -265,3 +279,48 @@ fn convert_ctime(t C.tm) Time { unix: make_unix_time(t) } } + +// A lot of these are taken from the Go library +pub type Duration i64 + +pub const( + nanosecond = Duration(1) + microsecond = Duration(1000) * nanosecond + millisecond = Duration(1000) * microsecond + second = Duration(1000) * millisecond + minute = Duration(60) * second + hour = Duration(60) * minute +) + +// nanoseconds returns the duration as an integer number of nanoseconds. +pub fn (d Duration) nanoseconds() i64 { return i64(d) } + +// microseconds returns the duration as an integer number of microseconds. +pub fn (d Duration) microseconds() i64 { return i64(d) / 1000 } + +// milliseconds returns the duration as an integer number of milliseconds. +pub fn (d Duration) milliseconds() i64 { return i64(d) / 1_000_000 } + +// The following functions return floating point numbers because it's common to +// consider all of them in sub-one intervals + +// seconds returns the duration as a floating point number of seconds. +pub fn (d Duration) seconds() f64 { + sec := d / second + nsec := d % second + return f64(sec) + f64(nsec)/1e9 +} + +// minutes returns the duration as a floating point number of minutes. +pub fn (d Duration) minutes() f64 { + min := d / minute + nsec := d % minute + return f64(min) + f64(nsec)/(60*1e9) +} + +// hours returns the duration as a floating point number of hours. +pub fn (d Duration) hours() f64 { + hr := d / hour + nsec := d % hour + return f64(hr) + f64(nsec)/(60*60*1e9) +} diff --git a/vlib/time/time_darwin.c.v b/vlib/time/time_darwin.c.v new file mode 100644 index 0000000000..808f5f971d --- /dev/null +++ b/vlib/time/time_darwin.c.v @@ -0,0 +1,40 @@ +module time + +#include + +const ( + // start_time is needed on Darwin and Windows because of potential overflows + start_time = C.mach_absolute_time() + time_base = init_time_base() +) + +[typedef] +struct C.mach_timebase_info_data_t { + numer u64 + denom u64 +} + +fn C.mach_absolute_time() u64 +fn C.mach_timebase_info(&C.mach_timebase_info_data_t) + +struct InternalTimeBase { + numer u64 + denom u64 +} + +fn init_time_base() InternalTimeBase { + mut tb := C.mach_timebase_info_data_t{} + C.mach_timebase_info(&tb) + return InternalTimeBase{numer:tb.numer, denom:tb.denom} +} + +fn sys_mono_now_darwin() u64 { + tm := C.mach_absolute_time() + return mul_div(tm - start_time, time_base.numer, time_base.denom) +} + +fn mul_div(val, numer, denom u64) u64 { + q := val / denom + r := val % denom + return q * numer + r * numer / denom +} diff --git a/vlib/time/time_nix.c.v b/vlib/time/time_nix.c.v index a44651b464..5dc8e02fbf 100644 --- a/vlib/time/time_nix.c.v +++ b/vlib/time/time_nix.c.v @@ -17,3 +17,25 @@ fn C.timegm(&tm) time_t fn make_unix_time(t C.tm) int { return int(C.timegm(&t)) } + +type time_t voidptr + +// in most systems, these are __quad_t, which is an i64 +struct C.timespec { + tv_sec i64 + tv_nsec i64 +} + +// the first arg is defined in include/bits/types.h as `__S32_TYPE`, which is `int` +fn C.clock_gettime(int, &C.timespec) + +fn sys_mono_now() u64 { + $if macos { + return sys_mono_now_darwin() + } $else { + ts := C.timespec{} + C.clock_gettime(C.CLOCK_MONOTONIC, &ts) + return u64(ts.tv_sec) * 1_000_000_000 + u64(ts.tv_nsec) + } +} + diff --git a/vlib/time/time_windows.c.v b/vlib/time/time_windows.c.v index de4b21feef..4d4d13f884 100644 --- a/vlib/time/time_windows.c.v +++ b/vlib/time/time_windows.c.v @@ -12,8 +12,48 @@ struct C.tm { tm_sec int } +[typedef] +struct C.LARGE_INTEGER { + QuadPart i64 +} + +const ( + // start_time is needed on Darwin and Windows because of potential overflows + // Windows: don't store the LARGE_INTEGER, just the QuadPart + start_time = init_win_time_start() + freq_time = init_win_time_freq() +) + fn C._mkgmtime(&C.tm) time_t +fn C.QueryPerformanceCounter(&C.LARGE_INTEGER) C.BOOL + +fn C.QueryPerformanceFrequency(&C.LARGE_INTEGER) C.BOOL + fn make_unix_time(t C.tm) int { return int(C._mkgmtime(&t)) } + +fn init_win_time_freq() u64 { + mut f := C.LARGE_INTEGER{} + _ := C.QueryPerformanceFrequency(&f) + return u64(f.QuadPart) +} + +fn init_win_time_start() u64 { + mut s := C.LARGE_INTEGER{} + _ := C.QueryPerformanceCounter(&s) + return u64(s.QuadPart) +} + +fn sys_mono_now() u64 { + mut tm := C.LARGE_INTEGER{} + _ := C.QueryPerformanceCounter(&tm) // XP or later never fail + return mul_div(u64(tm.QuadPart) - start_time, 1_000_000_000, freq_time) +} + +fn mul_div(val, numer, denom u64) u64 { + q := val / denom + r := val % denom + return q * numer + r * numer / denom +} diff --git a/vlib/time/timer.v b/vlib/time/timer.v deleted file mode 100644 index dc7bdde059..0000000000 --- a/vlib/time/timer.v +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. -// Use of this source code is governed by an MIT license -// that can be found in the LICENSE file. -module time - -// Timer struct and functions -// start and end are # of ms since start of system. -// When end_ticks == 0, this means the timer is actively "running" -pub struct Timer { - pause_start i64 -pub: - start_ticks i64 - end_ticks i64 -} - -// start Starts the timer. If the timer was paused, restarts counting. -pub fn (t mut Timer) start() { - if t.pause_start == 0 { - t.start_ticks = ticks() - } else { - // We were paused, so pretend like the time we were paused didn't - // happen - t.start_ticks += ticks() - t.pause_start - } - t.end_ticks = 0 - t.pause_start = 0 -} - -pub fn (t mut Timer) restart() { - t.end_ticks = 0 - t.pause_start = 0 - t.start_ticks = ticks() -} - -pub fn (t mut Timer) pause() { - t.pause_start = ticks() - t.end_ticks = t.pause_start // so elapsed() still keeps track of the cared-amount of time -} - -pub fn (t mut Timer) stop() { - t.end_ticks = ticks() - t.pause_start = 0 -} - -// elapsed If the Timer is stopped, returns the number of milliseconds between -// the last start and stop. -// If the Timer is still running, returns the number of milliseconds from the -// last start to now. -pub fn (t Timer) elapsed() i64 { - if t.end_ticks == 0 { - return ticks() - t.start_ticks - } else { - return t.end_ticks - t.start_ticks - } -} - -pub fn new_timer() Timer { - return Timer{start_ticks: ticks()} -} -