Skip to content

Commit ca9e5a9

Browse files
committed
rpc: remove another sort from VerifyClockOffsets
We can calculate the median absolute deviation without re-sorting. The code is a bit more complicated, but it does seem to be a win for the benchmark. ``` goos: darwin goarch: arm64 cpu: Apple M1 Pro │ before.txt │ after.txt │ │ sec/op │ sec/op vs base │ VerifyClockOffset 101.90µ ± 2% 84.69µ ± 20% -16.88% (p=0.009 n=20) │ before.txt │ after.txt │ │ B/op │ B/op vs base │ VerifyClockOffset 16.10Ki ± 0% 16.07Ki ± 0% -0.19% (p=0.000 n=20) │ before.txt │ after.txt │ │ allocs/op │ allocs/op vs base │ VerifyClockOffset 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=20) ¹ ¹ all samples are equal ``` Epic: none Release note: None
1 parent 3d8fe33 commit ca9e5a9

File tree

2 files changed

+30
-16
lines changed

2 files changed

+30
-16
lines changed

pkg/rpc/clock_offset.go

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,7 @@ func (r *RemoteClockMonitor) VerifyClockOffset(ctx context.Context) error {
369369
mean := sum / float64(len(offsets))
370370
stdDev := StandardDeviationPopulationKnownMean(offsets, mean)
371371
median := MedianSortedInput(offsets)
372-
// WARNING: offsets is unusable after this point as it has been modified.
373-
medianAbsoluteDeviation := MedianAbsoluteDeviationPopulationSortedInputMutatesInput(offsets)
372+
medianAbsoluteDeviation := MedianAbsoluteDeviationPopulationSortedInput(offsets)
374373

375374
r.metrics.ClockOffsetMeanNanos.Update(int64(mean))
376375
r.metrics.ClockOffsetStdDevNanos.Update(int64(stdDev))
@@ -517,21 +516,36 @@ func MedianSortedInput(sortedInput stats.Float64Data) float64 {
517516
}
518517
}
519518

520-
// MedianAbsoluteDeviationPopulationSortedInputMutatesInput calculates the
521-
// median absolute deviation from a pre-sorted population.
522-
//
523-
// WARNING: This function mutates its input to avoid further allocations.
524-
func MedianAbsoluteDeviationPopulationSortedInputMutatesInput(
525-
sortedInput stats.Float64Data,
526-
) float64 {
527-
if sortedInput.Len() == 0 {
519+
// MedianAbsoluteDeviationPopulationSortedInput calculates the median absolute
520+
// deviation from a pre-sorted population.
521+
func MedianAbsoluteDeviationPopulationSortedInput(sortedInput stats.Float64Data) float64 {
522+
switch sortedInput.Len() {
523+
case 0:
528524
return math.NaN()
525+
case 1:
526+
return 0
529527
}
530528

531529
m := MedianSortedInput(sortedInput)
532-
for key, value := range sortedInput {
533-
sortedInput[key] = math.Abs(value - m)
530+
a := sortedInput
531+
532+
// Peal off the largest difference on either end until we reach the midpoint(s).
533+
last := 0.0
534+
for len(a) > (len(sortedInput) / 2) {
535+
leftDiff := m - a[0]
536+
rightDiff := a[len(a)-1] - m
537+
if leftDiff >= rightDiff {
538+
last = leftDiff
539+
a = a[1:]
540+
} else {
541+
last = rightDiff
542+
a = a[:len(a)-1]
543+
}
544+
}
545+
546+
if len(sortedInput)%2 == 1 {
547+
return last
548+
} else {
549+
return (max(m-a[0], a[len(a)-1]-m) + last) * 0.5
534550
}
535-
sort.Float64s(sortedInput)
536-
return MedianSortedInput(sortedInput)
537551
}

pkg/rpc/clock_offset_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,8 @@ func TestStatsFuncs(t *testing.T) {
321321
floatWithinReasonableTolerance(t, theirVar, ourVar)
322322
})
323323

324-
t.Run("MedianAbsoluteDeviationPopulationSortedInputMutatesInput", func(t *testing.T) {
325-
ourMedAbsDev := MedianAbsoluteDeviationPopulationSortedInputMutatesInput(sortedData)
324+
t.Run("MedianAbsoluteDeviationPopulationSortedInput", func(t *testing.T) {
325+
ourMedAbsDev := MedianAbsoluteDeviationPopulationSortedInput(sortedData)
326326
theirMedianAbsDev, err := stats.MedianAbsoluteDeviationPopulation(data)
327327
require.NoError(t, err)
328328
floatWithinReasonableTolerance(t, theirMedianAbsDev, ourMedAbsDev)

0 commit comments

Comments
 (0)