diff options
Diffstat (limited to 'qualityattenuation/qualityattenuation.go')
| -rw-r--r-- | qualityattenuation/qualityattenuation.go | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/qualityattenuation/qualityattenuation.go b/qualityattenuation/qualityattenuation.go index 279959e..c6a2f85 100644 --- a/qualityattenuation/qualityattenuation.go +++ b/qualityattenuation/qualityattenuation.go @@ -9,6 +9,39 @@ import ( "github.com/influxdata/tdigest" ) +type cablelabsHist struct { + hist [256]float64 +} + +func (h *cablelabsHist) GetHistogram() [256]float64 { + return h.hist +} + +func (h *cablelabsHist) AddSample(sample float64) error { + bin := 0 + if sample < 0.050 { + // Round down + bin = int(sample / 0.0005) + h.hist[bin]++ + } else if sample < 0.150 { + bin = int((sample - 0.050) / 0.001) + h.hist[100+bin]++ + } else if sample < 1.150 { + bin = int((sample - 0.150) / 0.020) + h.hist[200+bin]++ + } else if sample < 1.400 { + bin = 250 + h.hist[bin]++ + } else if sample < 3.000 { + bin = int((sample - 1.400) / 0.400) + h.hist[251+bin]++ + } else { + bin = 255 + h.hist[bin]++ + } + return nil +} + type SimpleQualityAttenuation struct { empiricalDistribution *tdigest.TDigest offset float64 @@ -21,6 +54,16 @@ type SimpleQualityAttenuation struct { maximumLatency float64 } +type percentileLatencyPair struct { + percentile float64 + perfectLatency float64 + uselessLatency float64 +} + +type qualityRequirement struct { + latencyRequirements []percentileLatencyPair +} + func NewSimpleQualityAttenuation() *SimpleQualityAttenuation { return &SimpleQualityAttenuation{ empiricalDistribution: tdigest.NewWithCompression(50), @@ -108,6 +151,32 @@ func (qa *SimpleQualityAttenuation) GetPDV(percentile float64) float64 { return qa.GetPercentile(percentile) - qa.GetMinimum() } +func (qa *SimpleQualityAttenuation) PrintCablelabsStatisticsSummary() string { + // Prints a digest based on Cablelabs Latency Measurements Metrics and Architeture, CL-TR-LM-Arch-V01-221123, https://www.cablelabs.com/specifications/CL-TR-LM-Arch + // The recommendation is to report the following percentiles: 0, 10, 25, 50, 75, 90, 95, 99, 99.9 and 100 + return fmt.Sprintf("Cablelabs Statistics Summary:\n"+ + "0th Percentile: %f\n"+ + "10th Percentile: %f\n"+ + "25th Percentile: %f\n"+ + "50th Percentile: %f\n"+ + "75th Percentile: %f\n"+ + "90th Percentile: %f\n"+ + "95th Percentile: %f\n"+ + "99th Percentile: %f\n"+ + "99.9th Percentile: %f\n"+ + "100th Percentile: %f\n", + qa.GetPercentile(0.0), + qa.GetPercentile(10.0), + qa.GetPercentile(25.0), + qa.GetPercentile(50.0), + qa.GetPercentile(75.0), + qa.GetPercentile(90.0), + qa.GetPercentile(95.0), + qa.GetPercentile(99.0), + qa.GetPercentile(99.9), + qa.GetPercentile(100.0)) +} + // Merge two quality attenuation values. This operation assumes the two samples have the same offset and latency_eq_loss_threshold, and // will return an error if they do not. // It also assumes that the two quality attenuation values are measurements of the same thing (path, outcome, etc.). @@ -134,3 +203,64 @@ func (qa *SimpleQualityAttenuation) Merge(other *SimpleQualityAttenuation) error } return nil } + +func (qa *SimpleQualityAttenuation) EmpiricalDistributionHistogram() []float64 { + // Convert the tdigest to a histogram on the format defined by CableLabs, with the following bucket edges: + // 100 bins from 0 to 50 ms, each 0.5 ms wide + // 100 bins from 50 to 100 ms, each 1 ms wide + // 50 bins from 150 to 1150 ms, each 20 ms wide + // 1 bin from 1150 to 1400 ms, 250 ms wide + // 4 bins from 1400 to 3000 ms, each 400 ms wide + hist := make([]float64, 256) + for i := 0; i < 100; i++ { + hist[i] = float64(qa.numberOfSamples) * (qa.empiricalDistribution.CDF(float64(i+1)*0.0005) - qa.empiricalDistribution.CDF(float64(i)*0.0005)) + } + for i := 100; i < 200; i++ { + hist[i] = float64(qa.numberOfSamples) * (qa.empiricalDistribution.CDF(0.050+float64(i-99)*0.001) - qa.empiricalDistribution.CDF(0.050+float64(i-100)*0.001)) + } + for i := 200; i < 250; i++ { + hist[i] = float64(qa.numberOfSamples) * (qa.empiricalDistribution.CDF(0.150+float64(i-199)*0.020) - qa.empiricalDistribution.CDF(0.150+float64(i-200)*0.020)) + } + for i := 250; i < 251; i++ { + hist[i] = float64(qa.numberOfSamples) * (qa.empiricalDistribution.CDF(1.150+0.250) - qa.empiricalDistribution.CDF(1.150)) + } + for i := 251; i < 255; i++ { + hist[i] = float64(qa.numberOfSamples) * (qa.empiricalDistribution.CDF(1.400+float64(i-250)*0.400) - qa.empiricalDistribution.CDF(1.400+float64(i-251)*0.400)) + } + hist[255] = float64(qa.numberOfSamples) * (1 - qa.empiricalDistribution.CDF(3.000)) + return hist +} + +// Compute the Quality of Outcome (QoO) for a given quality requirement. +// The details and motivation for the QoO metric are described in the following internet draft: +// https://datatracker.ietf.org/doc/draft-olden-ippm-qoo/ +func (qa *SimpleQualityAttenuation) QoO(requirement qualityRequirement) float64 { + QoO := 100.0 + for _, percentileLatencyPair := range requirement.latencyRequirements { + score := 0.0 + percentile := percentileLatencyPair.percentile + perfectLatency := percentileLatencyPair.perfectLatency + uselessLatency := percentileLatencyPair.uselessLatency + latency := qa.GetPercentile(percentile) + if latency >= uselessLatency { + score = 0.0 + } else if latency <= perfectLatency { + score = 100.0 + } else { + score = 100 * ((uselessLatency - latency) / (uselessLatency - perfectLatency)) + } + if score < QoO { + QoO = score + } + } + return QoO +} + +func (qa *SimpleQualityAttenuation) GetGamingQoO() float64 { + qualReq := qualityRequirement{} + qualReq.latencyRequirements = []percentileLatencyPair{} + qualReq.latencyRequirements = append(qualReq.latencyRequirements, percentileLatencyPair{percentile: 50.0, perfectLatency: 0.030, uselessLatency: 0.150}) + qualReq.latencyRequirements = append(qualReq.latencyRequirements, percentileLatencyPair{percentile: 95.0, perfectLatency: 0.065, uselessLatency: 0.200}) + qualReq.latencyRequirements = append(qualReq.latencyRequirements, percentileLatencyPair{percentile: 99.0, perfectLatency: 0.100, uselessLatency: 0.250}) + return qa.QoO(qualReq) +} |
