github.com/network-quality/goresponsiveness@v0.0.0-20240129151524-343954285090/rpm/calculations.go (about) 1 /* 2 * This file is part of Go Responsiveness. 3 * 4 * Go Responsiveness is free software: you can redistribute it and/or modify it under 5 * the terms of the GNU General Public License as published by the Free Software Foundation, 6 * either version 2 of the License, or (at your option) any later version. 7 * Go Responsiveness is distributed in the hope that it will be useful, but WITHOUT ANY 8 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 9 * PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 * 11 * You should have received a copy of the GNU General Public License along 12 * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>. 13 */ 14 15 package rpm 16 17 import ( 18 "fmt" 19 20 "github.com/network-quality/goresponsiveness/series" 21 "github.com/network-quality/goresponsiveness/utilities" 22 ) 23 24 type Rpm[Data utilities.Number] struct { 25 SelfRttsTotal int 26 ForeignRttsTotal int 27 SelfRttsTrimmed int 28 ForeignRttsTrimmed int 29 SelfProbeRttPN Data 30 ForeignProbeRttPN Data 31 SelfProbeRttMean float64 32 ForeignProbeRttMean float64 33 PNRpm float64 34 MeanRpm float64 35 } 36 37 func CalculateRpm[Data utilities.Number, Bucket utilities.Number]( 38 selfRtts series.WindowSeries[Data, Bucket], aggregatedForeignRtts series.WindowSeries[Data, Bucket], trimming uint, percentile uint, 39 ) *Rpm[Data] { 40 // There may be more than one round trip accumulated together. If that is the case, 41 // we will blow them apart in to three separate measurements and each one will just 42 // be 1 / 3. 43 foreignRtts := series.NewWindowSeries[Data, int](series.Forever, 0) 44 foreignBuckets := 0 45 for _, v := range aggregatedForeignRtts.GetValues() { 46 if utilities.IsSome(v) { 47 v := utilities.GetSome(v) 48 foreignRtts.Reserve(foreignBuckets) 49 foreignRtts.Reserve(foreignBuckets + 1) 50 foreignRtts.Reserve(foreignBuckets + 2) 51 foreignRtts.Fill(foreignBuckets, v/3) 52 foreignRtts.Fill(foreignBuckets+1, v/3) 53 foreignRtts.Fill(foreignBuckets+2, v/3) 54 foreignBuckets += 3 55 } 56 } 57 58 boundedSelfRtts := selfRtts.ExtractBoundedSeries() 59 60 // First, let's do a double-sided trim of the top/bottom 10% of our measurements. 61 selfRttsTotalCount, _ := boundedSelfRtts.Count() 62 foreignRttsTotalCount, _ := foreignRtts.Count() 63 64 _, selfProbeRoundTripTimeMean, selfRttsTrimmed := 65 series.TrimmedMean(boundedSelfRtts, int(trimming)) 66 _, foreignProbeRoundTripTimeMean, foreignRttsTrimmed := 67 series.TrimmedMean(foreignRtts, int(trimming)) 68 69 selfRttsTrimmedCount := len(selfRttsTrimmed) 70 foreignRttsTrimmedCount := len(foreignRttsTrimmed) 71 72 // Second, let's do the P90 calculations. 73 _, selfProbeRoundTripTimePN := series.Percentile(boundedSelfRtts, percentile) 74 _, foreignProbeRoundTripTimePN := series.Percentile(foreignRtts, percentile) 75 76 // Note: The specification indicates that we want to calculate the foreign probes as such: 77 // 1/3*tcp_foreign + 1/3*tls_foreign + 1/3*http_foreign 78 // where tcp_foreign, tls_foreign, http_foreign are the P90 RTTs for the connection 79 // of the tcp, tls and http connections, respectively. However, we cannot break out 80 // the individual RTTs so we assume that they are roughly equal. 81 82 // This is 60 because we measure in seconds not ms 83 pnRpm := 60.0 / (float64(selfProbeRoundTripTimePN+foreignProbeRoundTripTimePN) / 2.0) 84 meanRpm := 60.0 / (float64(selfProbeRoundTripTimeMean+foreignProbeRoundTripTimeMean) / 2.0) 85 86 return &Rpm[Data]{ 87 SelfRttsTotal: selfRttsTotalCount, ForeignRttsTotal: foreignRttsTotalCount, 88 SelfRttsTrimmed: selfRttsTrimmedCount, ForeignRttsTrimmed: foreignRttsTrimmedCount, 89 SelfProbeRttPN: selfProbeRoundTripTimePN, ForeignProbeRttPN: foreignProbeRoundTripTimePN, 90 SelfProbeRttMean: selfProbeRoundTripTimeMean, ForeignProbeRttMean: foreignProbeRoundTripTimeMean, 91 PNRpm: pnRpm, MeanRpm: meanRpm, 92 } 93 } 94 95 func (rpm *Rpm[Data]) ToString() string { 96 return fmt.Sprintf( 97 `Total Self Probes: %d 98 Total Foreign Probes: %d 99 Trimmed Self Probes Count: %d 100 Trimmed Foreign Probes Count: %d 101 P90 Self RTT: %v 102 P90 Foreign RTT: %v 103 Trimmed Mean Self RTT: %f 104 Trimmed Mean Foreign RTT: %f 105 `, 106 rpm.SelfRttsTotal, 107 rpm.ForeignRttsTotal, 108 rpm.SelfRttsTrimmed, 109 rpm.ForeignRttsTrimmed, 110 rpm.SelfProbeRttPN, 111 rpm.ForeignProbeRttPN, 112 rpm.SelfProbeRttMean, 113 rpm.ForeignProbeRttMean, 114 ) 115 }