Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

Re: The most precise second (timer)

by haukex (Archbishop)
on Nov 27, 2019 at 12:13 UTC ( [id://11109306]=note: print w/replies, xml ) Need Help??


in reply to The most precise second (timer)

The interval must be as precise as possible.

I think that this is causing some confusion in this thread, and we should take a moment to define the problem better. See e.g. Accuracy and precision - as several people have commented, if you want precise measurement of time, then really the only way to go about it is a real-time OS and/or other custom circuitry connected to an atomic clock, high-quality GPS receiver, etc. However:

We have monitoring scripts that need to be run every second. The scripts execution takes longer than a second, but that is not a problem. ... I know that the preciseness depends on CPU, RAM, CMOS etc. I don’t care about these.

This, along with the code loop you showed, leads me to believe that precision isn't the central point of your question, it seems to me more like you're worried about a systematic and cumulative error, which would mean you're actually more worried about trueness.

In other words: You want something like for (1..100) { sleep( 1 + $correction_factor ) } to take as close to 100 wall clock seconds as possible. And at the same time, perhaps it's not so important to you if each delay has e.g. ±0.1 seconds precision, as long as they even out, in other words, it's ok if 10 of the delays take 1.1 seconds, as long as 10 other delays take 0.9 seconds.

Is this interpretation of your problem correct? In that case, IMHO sleep is not the right tool for the job, as I showed you'll need something that works with the computer's wall clock time.

And keeping the computer's wall clock time in sync with real-world time is a whole different can of worms! But in general that's what NTP is for, and if you're having trouble with that, as you seem to indicate, then perhaps it's best to look at that as a separate topic. (Update: Actually, on a second reading, apparently it's the corrections made to the system clock by NTP that seem to be giving you the trouble? That seems strange, if you're really running chronyd and no other NTP client on this machine. But in any case, everything above still stands.)

Update: I'm putting this in my top level reply because IMHO it is the best answer to your question: I only realized further down in this thread that clock_nanosleep from Time::HiRes is probably what you're looking for.

Replies are listed 'Best First'.
Re^2: The most precise second (timer)
by tukusejssirs (Beadle) on Nov 27, 2019 at 13:57 UTC

    haukex, you’re the closest to understanding my problem and possibly to give me a solution!

    Well, on the chronyd stuff: I am running chronyd service, but not running ntpd (which is not even installed on particular system), see bellow.

    $ systemctl status chronyd ● chronyd.service - NTP client/server Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; v +endor preset: enabled) Active: active (running) since Wed 2019-10-23 13:38:15 CEST; 1 mont +hs 4 days ago Docs: man:chronyd(8) man:chrony.conf(5) Main PID: 8102 (chronyd) CGroup: /system.slice/chronyd.service └─8102 /usr/sbin/chronyd Warning: Journal has been rotated since unit was started. Log output i +s incomplete or unavailable. $ systemctl status ntpd Unit ntpd.service could not be found.

    I still need to delve into the chronyd docs.

    Is this interpretation of your problem correct?

    Well, kind of. We’d like to monitor some stuff each and every second. In the perfect world, this would mean that each and every real second a script would run, get data and insert in into the DB. As we don’t want/need to use atomic clock (etc), therefore by the real second we mean a second as precice accurate as possible. … In the end, you are probably right about the interpretation. ;)

    So if sleep is not the right tool for the job, what would it be? And why does the EV (my $w2 = EV::periodic( 0, 1, 0, sub { print Math::BigFloat->new( sprintf("%d.%06d",gettimeofday) ), "\n";} );) seconds diff is greater than the original code (below is simplified version of the original code; on my computer it has diff bellow 0.0004, sometimes the avg is 0.0002)?

    use strict; use warnings; use Time::HiRes qw/time sleep/; sub negative_timer { my $max = $_[0]; my @time_test; my $start = time(); for (my $i = 0; $i < $max; $i++) { $start += 1; sleep $start - time(); $time_test[$i] = time(); } return @time_test; }
      I am running chronyd service, but not running ntpd

      Yes, AFAIK the two are mutually exclusive. I updated my node here to suggest some of the chrony configuration directives to look at that might be relevant for clock jumps.

      So if sleep is not the right tool for the job, what would it be?

      I checked, and interestingly, libev uses nanosleep(2) (or select(2)), which is apparently quite similar to what Time::HiRes's usleep does - so the method actually isn't as fundamentally different from your approach as I initially thought (but Perl's built-in sleep is of course too inaccurate). However, in libev, the calculations for the next event time happen at the C level, so I think it could be a little bit more accurate than if you do the same on the Perl level.

      And why does the EV ... seconds diff is greater than the original code (below is simplified version of the original code; on my computer it has diff bellow 0.0004, sometimes the avg is 0.0002)?

      The difference might just be from inaccuracies in the calculation of the sleep time, or the time period over which you measured (e.g. there might have been different jitter during the two). I ran a test where I ran my code from here over almost an hour, with the addition that I wrote out the timestamp for each time the event fired. Although my gettimeofday on each event was on average 0.0017s after the whole second (max 0.0021), I could see no signs of a cumulative error - perhaps it would be best if you run your own tests over a longer period of time (like a day) to see if it suits your needs.

      Update: Oh, and Average over 3312 deltas: 1.00001844328777194271613548903407496361, I'd hope you'd see this number shrink over an even longer test. Also minor edits.

        I think you led me to the right direction: nanosecond.

        I used the following script and compared the results with the original code. When averaging differences of 100 seconds (each script), the original comes out a bit better, but when averaging diffs of 300 seconds, the new script is better.

        use strict; use warnings; use Math::BigFloat; use Time::HiRes qw(gettimeofday sleep nanosleep); ... sub old_script { my $max = $_[0]; my @time_test; my $start = time(); for (my $i = 0; $i < $max; $i++) { $start += 1; sleep $start - time(); $time_test[$i] = Math::BigFloat->new(sprintf("%d.%06d", gettim +eofday)); } return @time_test; } sub new_script { my $max = $_[0]; my @time_test; for (my $i = 0; $i < $max; $i++) { nanosleep (1000000000); $time_test[$i] = Math::BigFloat->new(sprintf("%d.%06d", gettim +eofday)); } return @time_test; }

        The averages:

        # 100 s each script old_script_values_avg : 1.000479131313131313131313131313131313131 new_script_values_avg : 1.001016101010101010101010101010101010101 # 300 s each script old_script_values_avg : 0.9974391538461538461538461538461538461538 new_script_values_avg : 1.000457983277591973244147157190635451505

        I believe I cannot get better results without atomic clock. Correct me if I’m wrong.

        Anyway, thanks for the help and your time!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11109306]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (6)
As of 2024-04-25 15:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found