summaryrefslogtreecommitdiff
path: root/rpm/calculations.go
blob: 851e168aef464aff78a06dc344c4b237e97b725d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
 * This file is part of Go Responsiveness.
 *
 * Go Responsiveness is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software Foundation,
 * either version 2 of the License, or (at your option) any later version.
 * Go Responsiveness is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with Go Responsiveness. If not, see <https://www.gnu.org/licenses/>.
 */

package rpm

import (
	"fmt"

	"github.com/network-quality/goresponsiveness/series"
	"github.com/network-quality/goresponsiveness/utilities"
)

type Rpm[Data utilities.Number] struct {
	SelfRttsTotal       int
	ForeignRttsTotal    int
	SelfRttsTrimmed     int
	ForeignRttsTrimmed  int
	SelfProbeRttPN      Data
	ForeignProbeRttPN   Data
	SelfProbeRttMean    float64
	ForeignProbeRttMean float64
	PNRpm               float64
	MeanRpm             float64
}

func CalculateRpm[Data utilities.Number, Bucket utilities.Number](
	selfRtts series.WindowSeries[Data, Bucket], aggregatedForeignRtts series.WindowSeries[Data, Bucket], trimming uint, percentile uint,
) *Rpm[Data] {
	// There may be more than one round trip accumulated together. If that is the case,
	// we will blow them apart in to three separate measurements and each one will just
	// be 1 / 3.
	foreignRtts := series.NewWindowSeries[Data, int](series.Forever, 0)
	foreignBuckets := 0
	for _, v := range aggregatedForeignRtts.GetValues() {
		if utilities.IsSome(v) {
			v := utilities.GetSome(v)
			foreignRtts.Reserve(foreignBuckets)
			foreignRtts.Reserve(foreignBuckets + 1)
			foreignRtts.Reserve(foreignBuckets + 2)
			foreignRtts.Fill(foreignBuckets, v/3)
			foreignRtts.Fill(foreignBuckets+1, v/3)
			foreignRtts.Fill(foreignBuckets+2, v/3)
			foreignBuckets += 3
		}
	}

	boundedSelfRtts := selfRtts.ExtractBoundedSeries()

	// First, let's do a double-sided trim of the top/bottom 10% of our measurements.
	selfRttsTotalCount, _ := boundedSelfRtts.Count()
	foreignRttsTotalCount, _ := foreignRtts.Count()

	_, selfProbeRoundTripTimeMean, selfRttsTrimmed :=
		series.TrimmedMean(boundedSelfRtts, int(trimming))
	_, foreignProbeRoundTripTimeMean, foreignRttsTrimmed :=
		series.TrimmedMean(foreignRtts, int(trimming))

	selfRttsTrimmedCount := len(selfRttsTrimmed)
	foreignRttsTrimmedCount := len(foreignRttsTrimmed)

	// Second, let's do the P90 calculations.
	_, selfProbeRoundTripTimePN := series.Percentile(boundedSelfRtts, percentile)
	_, foreignProbeRoundTripTimePN := series.Percentile(foreignRtts, percentile)

	// Note: The specification indicates that we want to calculate the foreign probes as such:
	// 1/3*tcp_foreign + 1/3*tls_foreign + 1/3*http_foreign
	// where tcp_foreign, tls_foreign, http_foreign are the P90 RTTs for the connection
	// of the tcp, tls and http connections, respectively. However, we cannot break out
	// the individual RTTs so we assume that they are roughly equal.

	// This is 60 because we measure in seconds not ms
	pnRpm := 60.0 / (float64(selfProbeRoundTripTimePN+foreignProbeRoundTripTimePN) / 2.0)
	meanRpm := 60.0 / (float64(selfProbeRoundTripTimeMean+foreignProbeRoundTripTimeMean) / 2.0)

	return &Rpm[Data]{
		SelfRttsTotal: selfRttsTotalCount, ForeignRttsTotal: foreignRttsTotalCount,
		SelfRttsTrimmed: selfRttsTrimmedCount, ForeignRttsTrimmed: foreignRttsTrimmedCount,
		SelfProbeRttPN: selfProbeRoundTripTimePN, ForeignProbeRttPN: foreignProbeRoundTripTimePN,
		SelfProbeRttMean: selfProbeRoundTripTimeMean, ForeignProbeRttMean: foreignProbeRoundTripTimeMean,
		PNRpm: pnRpm, MeanRpm: meanRpm,
	}
}

func (rpm *Rpm[Data]) ToString() string {
	return fmt.Sprintf(
		`Total Self Probes:            %d
Total Foreign Probes:         %d
Trimmed Self Probes Count:    %d
Trimmed Foreign Probes Count: %d
P90 Self RTT:                 %v
P90 Foreign RTT:              %v
Trimmed Mean Self RTT:        %f
Trimmed Mean Foreign RTT:     %f
`,
		rpm.SelfRttsTotal,
		rpm.ForeignRttsTotal,
		rpm.SelfRttsTrimmed,
		rpm.ForeignRttsTrimmed,
		rpm.SelfProbeRttPN,
		rpm.ForeignProbeRttPN,
		rpm.SelfProbeRttMean,
		rpm.ForeignProbeRttMean,
	)
}