From b05fbaacdaf8ffe37ad04f9d4439b06d8b538869 Mon Sep 17 00:00:00 2001 From: hikari Date: Sat, 16 Apr 2022 00:32:17 +0300 Subject: [PATCH 1/6] time: add accurate sleep procedure --- core/time/time.odin | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/core/time/time.odin b/core/time/time.odin index fddb09d85..7056f83f8 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -213,6 +213,37 @@ time_add :: proc(t: Time, d: Duration) -> Time { return Time{t._nsec + i64(d)} } +// Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/ +accurate_sleep :: proc(d: Duration) { + to_sleep, estimate, mean, m2, count: Duration + + to_sleep = d + estimate = 5 * Millisecond + mean = 5 * Millisecond + count = 1 + + for to_sleep > estimate { + start := tick_now() + sleep(1 * Millisecond) + + observed := tick_since(start) + to_sleep -= observed + + count += 1 + + delta := observed - mean + mean += delta / count + m2 += delta * (observed - mean) + stddev := intrinsics.sqrt(f64(m2) / f64(count - 1)) + estimate = mean + Duration(stddev) + } + + start := tick_now() + for to_sleep > tick_since(start) { + intrinsics.cpu_relax() // prevent the spinlock from taking the thread hostage, still accurate enough + // NOTE: it is possible that sometimes cpu can relax a bit too much, in that case it should spinlock freely for a while (TODO) + } +} ABSOLUTE_ZERO_YEAR :: i64(-292277022399) // Day is chosen so that 2001-01-01 is Monday in the calculations ABSOLUTE_TO_INTERNAL :: i64(-9223371966579724800) // i64((ABSOLUTE_ZERO_YEAR - 1) * 365.2425 * SECONDS_PER_DAY); From b9dc81d808af3cd4db69bca8cffc0f3bbb48ef8a Mon Sep 17 00:00:00 2001 From: hikari Date: Sat, 16 Apr 2022 13:46:36 +0300 Subject: [PATCH 2/6] runtime: update comment description --- core/runtime/core.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 4ab21d8cf..4269450de 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -401,6 +401,7 @@ Raw_Cstring :: struct { Linux, Essence, FreeBSD, + OpenBSD, WASI, JS, Freestanding, From 0a0440a6e869e8d8791608f14fb55f37f323a23e Mon Sep 17 00:00:00 2001 From: hikari Date: Sat, 16 Apr 2022 14:08:37 +0300 Subject: [PATCH 3/6] time: yield accurate_sleep instead of relaxing the cpu --- core/time/time.odin | 10 ++++++++-- core/time/time_unix.odin | 6 ++++++ core/time/time_windows.odin | 4 ++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/core/time/time.odin b/core/time/time.odin index 7056f83f8..f2e974647 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -213,6 +213,10 @@ time_add :: proc(t: Time, d: Duration) -> Time { return Time{t._nsec + i64(d)} } +yield :: proc "contextless" () { + _yield() +} + // Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/ accurate_sleep :: proc(d: Duration) { to_sleep, estimate, mean, m2, count: Duration @@ -240,8 +244,10 @@ accurate_sleep :: proc(d: Duration) { start := tick_now() for to_sleep > tick_since(start) { - intrinsics.cpu_relax() // prevent the spinlock from taking the thread hostage, still accurate enough - // NOTE: it is possible that sometimes cpu can relax a bit too much, in that case it should spinlock freely for a while (TODO) + // prevent the spinlock from taking the thread hostage, still accurate enough + yield() + // NOTE: it might be possible that it yields for too long, in that case it should spinlock freely for a while + // TODO: needs actual testing done to check if that's the case } } diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin index 37fc1fd3e..db15a824a 100644 --- a/core/time/time_unix.odin +++ b/core/time/time_unix.odin @@ -1,6 +1,8 @@ //+build linux, darwin, freebsd, openbsd package time +import "core:sys/unix" + IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC. when ODIN_OS == .Darwin { @@ -17,6 +19,10 @@ foreign libc { @(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 --- } +_yield :: proc "contextless" () { + unix.sched_yield() +} + TimeSpec :: struct { tv_sec : i64, /* seconds */ tv_nsec : i64, /* nanoseconds */ diff --git a/core/time/time_windows.odin b/core/time/time_windows.odin index 0fb9eaa0f..397741126 100644 --- a/core/time/time_windows.odin +++ b/core/time/time_windows.odin @@ -35,3 +35,7 @@ _tick_now :: proc "contextless" () -> Tick { _nsec := mul_div_u64(i64(now), 1e9, i64(qpc_frequency)) return Tick{_nsec = _nsec} } + +_yield :: proc "contextless" () { + win32.SwitchToThread() +} From 4f4793817cfb1782c1b2e6cf55ffdbfb01bcffaf Mon Sep 17 00:00:00 2001 From: hikari Date: Sat, 16 Apr 2022 14:35:49 +0300 Subject: [PATCH 4/6] time: fix unix build --- core/time/time_unix.odin | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin index db15a824a..0cfa196a2 100644 --- a/core/time/time_unix.odin +++ b/core/time/time_unix.odin @@ -1,8 +1,6 @@ //+build linux, darwin, freebsd, openbsd package time -import "core:sys/unix" - IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC. when ODIN_OS == .Darwin { @@ -19,8 +17,18 @@ foreign libc { @(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 --- } +foreign import "system:pthread" + +import "core:c" + +@(private="file") +@(default_calling_convention="c") +foreign pthread { + sched_yield :: proc() -> c.int --- +} + _yield :: proc "contextless" () { - unix.sched_yield() + sched_yield() } TimeSpec :: struct { From 8bf73950fa3bfaea940f8c14e69824b06908aa5e Mon Sep 17 00:00:00 2001 From: hikari Date: Sat, 16 Apr 2022 14:36:48 +0300 Subject: [PATCH 5/6] time: remove unnecessary yield --- core/time/time.odin | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/time/time.odin b/core/time/time.odin index f2e974647..ac233fbd2 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -213,10 +213,6 @@ time_add :: proc(t: Time, d: Duration) -> Time { return Time{t._nsec + i64(d)} } -yield :: proc "contextless" () { - _yield() -} - // Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/ accurate_sleep :: proc(d: Duration) { to_sleep, estimate, mean, m2, count: Duration @@ -245,7 +241,7 @@ accurate_sleep :: proc(d: Duration) { start := tick_now() for to_sleep > tick_since(start) { // prevent the spinlock from taking the thread hostage, still accurate enough - yield() + _yield() // NOTE: it might be possible that it yields for too long, in that case it should spinlock freely for a while // TODO: needs actual testing done to check if that's the case } From 91037766d2b583ac34d23eee5a46f10df9d3cba0 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 16 Apr 2022 16:48:08 +0200 Subject: [PATCH 6/6] Update time.odin Add caveats. --- core/time/time.odin | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/time/time.odin b/core/time/time.odin index ac233fbd2..1f778a8de 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -214,12 +214,17 @@ time_add :: proc(t: Time, d: Duration) -> Time { } // Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/ +// +// Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case. +// On Windows it depends but is comparable with regular sleep in the worst case. +// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to +// tell Windows to use a more accurate timer for your process. accurate_sleep :: proc(d: Duration) { to_sleep, estimate, mean, m2, count: Duration to_sleep = d estimate = 5 * Millisecond - mean = 5 * Millisecond + mean = 5 * Millisecond count = 1 for to_sleep > estimate {