From f22c226634e4d7b96a01ed81225c0ba7cbe9aa0f Mon Sep 17 00:00:00 2001 From: ChaelCodes Date: Mon, 13 Nov 2023 21:50:44 +0000 Subject: [PATCH 1/2] Build custom sleep that is hyper-accurate to avoid timer resolution problems Co-authored-by: John Hawthorn --- ext/vernier/vernier.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ext/vernier/vernier.cc b/ext/vernier/vernier.cc index dd56ddc..6d038b3 100644 --- a/ext/vernier/vernier.cc +++ b/ext/vernier/vernier.cc @@ -72,13 +72,17 @@ class TimeStamp { return TimeStamp(0); } - static void Sleep(const TimeStamp &time) { - struct timespec ts = time.timespec(); + // SleepUntil a specified timestamp + // Highly accurate manual sleep time + static void SleepUntil(const TimeStamp &target_time) { + if (target_time.zero()) return; + struct timespec ts = target_time.timespec(); int res; do { - res = nanosleep(&ts, &ts); - } while (res && errno == EINTR); + // do nothing until it's time :) + sleep(0); + } while (target_time > TimeStamp::Now()); } static TimeStamp from_microseconds(uint64_t us) { @@ -1252,13 +1256,12 @@ class TimeCollector : public BaseCollector { next_sample_schedule += interval; + // If sampling falls behind, restart, and check in another interval if (next_sample_schedule < sample_complete) { - //fprintf(stderr, "fell behind by %ius\n", (sample_complete - next_sample_schedule).microseconds()); next_sample_schedule = sample_complete + interval; } - TimeStamp sleep_time = next_sample_schedule - sample_complete; - TimeStamp::Sleep(sleep_time); + TimeStamp::SleepUntil(next_sample_schedule); } thread_stopped.post(); From 56e3af13c99eedcaacab3a9328f9512df6d95731 Mon Sep 17 00:00:00 2001 From: ChaelCodes Date: Mon, 13 Nov 2023 21:54:14 +0000 Subject: [PATCH 2/2] Custom TEST_SLEEP_SCALE in Specs Some very slow systems fall behind on the sampling interval which causes tests to fail. This adds a TEST_SLEEP_SCALE to support slower systems. --- test/test_time_collector.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/test_time_collector.rb b/test/test_time_collector.rb index d4b09d7..a740b2c 100644 --- a/test/test_time_collector.rb +++ b/test/test_time_collector.rb @@ -3,8 +3,11 @@ require "test_helper" class TestTimeCollector < Minitest::Test + SLEEP_SCALE = ENV.fetch("TEST_SLEEP_SCALE", 0.1).to_f # seconds/100ms + SAMPLE_SCALE_INTERVAL = 10_000 * SLEEP_SCALE # Microseconds + def bar - sleep 0.1 + sleep SLEEP_SCALE end def foo @@ -28,7 +31,7 @@ def test_receives_gc_events end def test_time_collector - collector = Vernier::Collector.new(:wall, interval: 1000) + collector = Vernier::Collector.new(:wall, interval: SAMPLE_SCALE_INTERVAL) collector.start foo result = collector.stop @@ -45,7 +48,7 @@ def test_time_collector end def test_sleeping_threads - collector = Vernier::Collector.new(:wall, interval: 1000) + collector = Vernier::Collector.new(:wall, interval: SAMPLE_SCALE_INTERVAL) th1 = Thread.new { foo; Thread.current.native_thread_id } th2 = Thread.new { foo; Thread.current.native_thread_id } collector.start @@ -54,8 +57,10 @@ def test_sleeping_threads result = collector.stop tally = result.threads.transform_values do |thread| + # Number of samples thread[:weights].sum end.to_h + assert_in_epsilon 200, tally[Thread.current.native_thread_id], generous_epsilon assert_in_epsilon 200, tally[th1id], generous_epsilon assert_in_epsilon 200, tally[th2id], generous_epsilon