Add 50th and 95th percentiles
This commit is contained in:
parent
5853f97295
commit
752228ef62
@ -115,8 +115,12 @@ type Percentiles struct {
|
||||
Mean uint64 `json:"mean"`
|
||||
// Max seen over the collected sample.
|
||||
Max uint64 `json:"max"`
|
||||
// 50th percentile over the collected sample.
|
||||
Fifty uint64 `json:"fifty"`
|
||||
// 90th percentile over the collected sample.
|
||||
Ninety uint64 `json:"ninety"`
|
||||
// 95th percentile over the collected sample.
|
||||
NinetyFive uint64 `json:"ninetyfive"`
|
||||
}
|
||||
|
||||
type Usage struct {
|
||||
|
@ -34,14 +34,17 @@ func (a uint64Slice) Len() int { return len(a) }
|
||||
func (a uint64Slice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a uint64Slice) Less(i, j int) bool { return a[i] < a[j] }
|
||||
|
||||
// Get 90th percentile of the provided samples. Round to integer.
|
||||
func (self uint64Slice) Get90Percentile() uint64 {
|
||||
// Get percentile of the provided samples. Round to integer.
|
||||
func (self uint64Slice) GetPercentile(d float64) uint64 {
|
||||
if d < 0.0 || d > 1.0 {
|
||||
return 0
|
||||
}
|
||||
count := self.Len()
|
||||
if count == 0 {
|
||||
return 0
|
||||
}
|
||||
sort.Sort(self)
|
||||
n := float64(0.9 * (float64(count) + 1))
|
||||
n := float64(d * (float64(count) + 1))
|
||||
idx, frac := math.Modf(n)
|
||||
index := int(idx)
|
||||
percentile := float64(self[index-1])
|
||||
@ -94,20 +97,24 @@ func (self *resource) Add(p info.Percentiles) {
|
||||
// Add a single sample. Internally, we convert it to a fake percentile sample.
|
||||
func (self *resource) AddSample(val uint64) {
|
||||
sample := info.Percentiles{
|
||||
Present: true,
|
||||
Mean: val,
|
||||
Max: val,
|
||||
Ninety: val,
|
||||
Present: true,
|
||||
Mean: val,
|
||||
Max: val,
|
||||
Fifty: val,
|
||||
Ninety: val,
|
||||
NinetyFive: val,
|
||||
}
|
||||
self.Add(sample)
|
||||
}
|
||||
|
||||
// Get max, average, and 90p from existing samples.
|
||||
func (self *resource) GetPercentile() info.Percentiles {
|
||||
func (self *resource) GetAllPercentiles() info.Percentiles {
|
||||
p := info.Percentiles{}
|
||||
p.Mean = uint64(self.mean.Mean)
|
||||
p.Max = self.max
|
||||
p.Ninety = self.samples.Get90Percentile()
|
||||
p.Fifty = self.samples.GetPercentile(0.5)
|
||||
p.Ninety = self.samples.GetPercentile(0.9)
|
||||
p.NinetyFive = self.samples.GetPercentile(0.95)
|
||||
p.Present = true
|
||||
return p
|
||||
}
|
||||
@ -128,8 +135,8 @@ func GetDerivedPercentiles(stats []*info.Usage) info.Usage {
|
||||
memory.Add(stat.Memory)
|
||||
}
|
||||
usage := info.Usage{}
|
||||
usage.Cpu = cpu.GetPercentile()
|
||||
usage.Memory = memory.GetPercentile()
|
||||
usage.Cpu = cpu.GetAllPercentiles()
|
||||
usage.Memory = memory.GetAllPercentiles()
|
||||
return usage
|
||||
}
|
||||
|
||||
@ -183,7 +190,7 @@ func GetMinutePercentiles(stats []*secondSample) info.Usage {
|
||||
percent := getPercentComplete(stats)
|
||||
return info.Usage{
|
||||
PercentComplete: percent,
|
||||
Cpu: cpu.GetPercentile(),
|
||||
Memory: memory.GetPercentile(),
|
||||
Cpu: cpu.GetAllPercentiles(),
|
||||
Memory: memory.GetAllPercentiles(),
|
||||
}
|
||||
}
|
||||
|
@ -23,25 +23,29 @@ import (
|
||||
|
||||
const Nanosecond = 1000000000
|
||||
|
||||
func Test90Percentile(t *testing.T) {
|
||||
func assertPercentile(t *testing.T, s uint64Slice, f float64, want uint64) {
|
||||
if got := s.GetPercentile(f); got != want {
|
||||
t.Errorf("GetPercentile(%f) is %d, should be %d.", f, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPercentile(t *testing.T) {
|
||||
N := 100
|
||||
stats := make(uint64Slice, 0, N)
|
||||
s := make(uint64Slice, 0, N)
|
||||
for i := N; i > 0; i-- {
|
||||
stats = append(stats, uint64(i))
|
||||
s = append(s, uint64(i))
|
||||
}
|
||||
p := stats.Get90Percentile()
|
||||
if p != 90 {
|
||||
t.Errorf("90th percentile is %d, should be 90.", p)
|
||||
}
|
||||
// 90p should be between 94 and 95. Promoted to 95.
|
||||
assertPercentile(t, s, 0.2, 20)
|
||||
assertPercentile(t, s, 0.7, 70)
|
||||
assertPercentile(t, s, 0.9, 90)
|
||||
N = 105
|
||||
for i := 101; i <= N; i++ {
|
||||
stats = append(stats, uint64(i))
|
||||
}
|
||||
p = stats.Get90Percentile()
|
||||
if p != 95 {
|
||||
t.Errorf("90th percentile is %d, should be 95.", p)
|
||||
s = append(s, uint64(i))
|
||||
}
|
||||
// 90p should be between 94 and 95. Promoted to 95.
|
||||
assertPercentile(t, s, 0.2, 21)
|
||||
assertPercentile(t, s, 0.7, 74)
|
||||
assertPercentile(t, s, 0.9, 95)
|
||||
}
|
||||
|
||||
func TestMean(t *testing.T) {
|
||||
@ -74,19 +78,23 @@ func TestAggregates(t *testing.T) {
|
||||
usage := GetMinutePercentiles(stats)
|
||||
// Cpu mean, max, and 90p should all be 1000 ms/s.
|
||||
cpuExpected := info.Percentiles{
|
||||
Present: true,
|
||||
Mean: 1000,
|
||||
Max: 1000,
|
||||
Ninety: 1000,
|
||||
Present: true,
|
||||
Mean: 1000,
|
||||
Max: 1000,
|
||||
Fifty: 1000,
|
||||
Ninety: 1000,
|
||||
NinetyFive: 1000,
|
||||
}
|
||||
if usage.Cpu != cpuExpected {
|
||||
t.Errorf("cpu stats are %+v. Expected %+v", usage.Cpu, cpuExpected)
|
||||
}
|
||||
memExpected := info.Percentiles{
|
||||
Present: true,
|
||||
Mean: 50 * 1024,
|
||||
Max: 99 * 1024,
|
||||
Ninety: 90 * 1024,
|
||||
Present: true,
|
||||
Mean: 50 * 1024,
|
||||
Max: 99 * 1024,
|
||||
Fifty: 50 * 1024,
|
||||
Ninety: 90 * 1024,
|
||||
NinetyFive: 95 * 1024,
|
||||
}
|
||||
if usage.Memory != memExpected {
|
||||
t.Errorf("memory stats are mean %+v. Expected %+v", usage.Memory, memExpected)
|
||||
@ -119,19 +127,23 @@ func TestSamplesCloseInTimeIgnored(t *testing.T) {
|
||||
usage := GetMinutePercentiles(stats)
|
||||
// Cpu mean, max, and 90p should all be 1000 ms/s. All high-value samples are discarded.
|
||||
cpuExpected := info.Percentiles{
|
||||
Present: true,
|
||||
Mean: 1000,
|
||||
Max: 1000,
|
||||
Ninety: 1000,
|
||||
Present: true,
|
||||
Mean: 1000,
|
||||
Max: 1000,
|
||||
Fifty: 1000,
|
||||
Ninety: 1000,
|
||||
NinetyFive: 1000,
|
||||
}
|
||||
if usage.Cpu != cpuExpected {
|
||||
t.Errorf("cpu stats are %+v. Expected %+v", usage.Cpu, cpuExpected)
|
||||
}
|
||||
memExpected := info.Percentiles{
|
||||
Present: true,
|
||||
Mean: 50 * 1024,
|
||||
Max: 99 * 1024,
|
||||
Ninety: 90 * 1024,
|
||||
Present: true,
|
||||
Mean: 50 * 1024,
|
||||
Max: 99 * 1024,
|
||||
Fifty: 50 * 1024,
|
||||
Ninety: 90 * 1024,
|
||||
NinetyFive: 95 * 1024,
|
||||
}
|
||||
if usage.Memory != memExpected {
|
||||
t.Errorf("memory stats are mean %+v. Expected %+v", usage.Memory, memExpected)
|
||||
@ -146,35 +158,43 @@ func TestDerivedStats(t *testing.T) {
|
||||
s := &info.Usage{
|
||||
PercentComplete: 100,
|
||||
Cpu: info.Percentiles{
|
||||
Present: true,
|
||||
Mean: i * Nanosecond,
|
||||
Max: i * Nanosecond,
|
||||
Ninety: i * Nanosecond,
|
||||
Present: true,
|
||||
Mean: i * Nanosecond,
|
||||
Max: i * Nanosecond,
|
||||
Fifty: i * Nanosecond,
|
||||
Ninety: i * Nanosecond,
|
||||
NinetyFive: i * Nanosecond,
|
||||
},
|
||||
Memory: info.Percentiles{
|
||||
Present: true,
|
||||
Mean: i * 1024,
|
||||
Max: i * 1024,
|
||||
Ninety: i * 1024,
|
||||
Present: true,
|
||||
Mean: i * 1024,
|
||||
Max: i * 1024,
|
||||
Fifty: i * 1024,
|
||||
Ninety: i * 1024,
|
||||
NinetyFive: i * 1024,
|
||||
},
|
||||
}
|
||||
stats = append(stats, s)
|
||||
}
|
||||
usage := GetDerivedPercentiles(stats)
|
||||
cpuExpected := info.Percentiles{
|
||||
Present: true,
|
||||
Mean: 50 * Nanosecond,
|
||||
Max: 99 * Nanosecond,
|
||||
Ninety: 90 * Nanosecond,
|
||||
Present: true,
|
||||
Mean: 50 * Nanosecond,
|
||||
Max: 99 * Nanosecond,
|
||||
Fifty: 50 * Nanosecond,
|
||||
Ninety: 90 * Nanosecond,
|
||||
NinetyFive: 95 * Nanosecond,
|
||||
}
|
||||
if usage.Cpu != cpuExpected {
|
||||
t.Errorf("cpu stats are %+v. Expected %+v", usage.Cpu, cpuExpected)
|
||||
}
|
||||
memExpected := info.Percentiles{
|
||||
Present: true,
|
||||
Mean: 50 * 1024,
|
||||
Max: 99 * 1024,
|
||||
Ninety: 90 * 1024,
|
||||
Present: true,
|
||||
Mean: 50 * 1024,
|
||||
Max: 99 * 1024,
|
||||
Fifty: 50 * 1024,
|
||||
Ninety: 90 * 1024,
|
||||
NinetyFive: 95 * 1024,
|
||||
}
|
||||
if usage.Memory != memExpected {
|
||||
t.Errorf("memory stats are mean %+v. Expected %+v", usage.Memory, memExpected)
|
||||
|
Loading…
Reference in New Issue
Block a user