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