[ntp:hackers] 1 msec jitter on serial PPS tamed

Dave Hart davehart at davehart.com
Mon Feb 23 14:49:58 UTC 2009


Thank you for wasting a bit more time on this ancient patch
submission.  I re-read the thread from 2007 again in full
today and still did not understand the approach your code
took to determining which clock/counter sample from the
history to use.  Now I understand what it is doing in broad
terms and can dig into it again to better understand the
details.

Cheers,
Dave Hart

> -----Original Message-----
> From: Peter Rosin [mailto:peda at axentia.se]
> Sent: Monday, 23 February 2009 13:34
> To: Dave Hart; hackers at lists.ntp.org
> Subject: RE: [ntp:hackers] 1 msec jitter on serial PPS
tamed
> 
> > > Hmmm, sounds a bit like this stuff I wrote 5+ years
ago...
> > > https://support.ntp.org/bugs/show_bug.cgi?id=216
> >
> > I looked at the "Non-monotonic time on NT" thread in the
> > hackers archive from May 2007 briefly.  I've read all
those
> > archives recently, and that's why I had my attention to
> > drawn to this patch before.  And given up confused
before.
> >
> > I've stared at the patch again for a while and I
understand
> > more, but still not enough to know what it's trying to
do.
> > I understand that once per system clock tickover you
note
> > the increase in the performance counter since the last
> > go-round and feed that difference into your history,
which
> > is kept as one entry per system clock tick containing
the
> > counter delta for that tick.  I also understand that
somehow
> > some correction is computed and an altered performance
> > counter value is fed back into TimerApcFunction's now
surely
> > misnamed LargeIntNowCount to be used as the operative
> > baseline count for inteferring time and treated as if it
> > were captured from the performance counter at the
current
> > time.
> 
> LargeIntNowCount is not really misnamed, you just have to
> realize that the goal of that variable is to store the
> performance count when the tick occurred, not some point
in
> the middle of the tick. There are a whole bunch of values
> for LargeIntNowCount that are correct for every possible
> LastTimerTime. But if all it takes is a renamed variable,
> I'm of course all for it :-)
> 
> > I am lost as to how the history is consulted to
determine
> > the correction to the performance counter value to be
> > treated as corresponding to the current time.  The code
is
> > clean but the only comments are on things I could have
> > guessed, there is no overview.  Why does a function
called
> > perf_insert return something that has nothing to do with
> > inserting anything?  I'm sure it was perfectly clear to
you
> > what you were trying to do, but sometimes communication
is
> > required to synchronize the thinking of others.
> >
> > Please do shed some light.
> 
> The whole point of the patch is to select the sample of
> the performance counter that happened earliest in the
system
> clock tick, and use that as a base for future
extrapolations.
> (This is not interpolation as you are using the past to
> extrapolate into the future, even if the future is only
what
> is going on between now and the next system clock tick
that
> is about to happen very soon. I.e., you can't interpolate
> between A and B, if you don't know B.)
> 
> perf_insert inserts into the perf_filter[] history.
However,
> it *also* keeps track of the best sample. It does this by
> keeping some running variables in order to not loop over
the
> filter content on every insert. So, after the
> 
> 	perf_offset += diff * PERF_FILTER_SIZE - sum;
> 
> line (ignore the if (!perf_full) branch until you
understand
> what happens when the filter is full), perf_offset
indicates
> how good this sample is when compared to the currently
best
> sample (which is found in perf_offset_index in the
history).
> 
> The perf_offset update should be viewed as this: if the
current
> diff is less than the filter average (both scaled by the
filter
> length), then this sample is better than the previous (the
time
> between the previous sample and this sample is smaller
than the
> average, i.e. this sample happened earlier in its tick
than the
> previous sample did) and perf_offset is decreased. If the
current
> diff is larger than the filter average, this sample is
worse
> than the previous sample and perf_offset is increased.
> 
> When perf_offset is negative, this sample is so good that
it is
> better than the previous best. When that happens, use this
sample
> as the base in the future. That of course implies setting
perf_offset
> to zero, to make future tests for even better samples
work.
> 
> When perf_offset is positive it tells how bad this sample
is
> when compared to the best sample in the history, and this
fact
> is used to adjust the sample.
> 
> So, perf_insert does all the meat of the work. It inserts
into
> history, it keeps track of the best sample and it adjusts
the
> incoming samples so that they appear as good as the best
sample.
> 
> The perf_insert function also keeps track of the second
best
> sample to have something decent to fall back on when a
really
> good sample is expiring due to old age.
> 
> So, every time a better sample is found, gettimeofday()
takes
> a (hopefully) tiny step forward. And every time a really
good
> sample expires, gettimeofday() takes a (hopefully) tiny
step
> backwards. And I claim that, while not perfect, this is
much
> better than having a gettimeofday() that takes bigger
steps
> forward and back, and does so more frequently.
> 
> Maybe an unoptimized implementation that loops over the
filter
> content on every sample would help you understand what is
going
> on, but I have wasted enough energy on this already...
> 
> Cheers,
> Peter


More information about the hackers mailing list