Use ContainerStatsSample to store samples because CPU usages are

cumilative
This commit is contained in:
Nan Deng 2014-06-11 10:37:05 -07:00
parent d5f9cbc890
commit eaa166e8c3
2 changed files with 69 additions and 23 deletions

View File

@ -101,10 +101,22 @@ func (self *ContainerInfo) StatsEndTime() time.Time {
type CpuStats struct { type CpuStats struct {
Usage struct { Usage struct {
Total uint64 `json:"total"` // Number of nanoseconds of CPU time used by the container
// since the beginning. This is a ccumulative
// value, not an instantaneous value.
Total uint64 `json:"total"`
// Per CPU/core usage of the container.
// Unit: nanoseconds.
PerCpu []uint64 `json:"per_cpu,omitempty"` PerCpu []uint64 `json:"per_cpu,omitempty"`
User uint64 `json:"user"`
System uint64 `json:"system"` // How much time was spent in user space since beginning.
// Unit: nanoseconds
User uint64 `json:"user"`
// How much time was spent in kernel space since beginning.
// Unit: nanoseconds
System uint64 `json:"system"`
} `json:"usage"` } `json:"usage"`
Load int32 `json:"load"` Load int32 `json:"load"`
} }
@ -142,6 +154,18 @@ type ContainerStats struct {
Memory *MemoryStats `json:"memory,omitempty"` Memory *MemoryStats `json:"memory,omitempty"`
} }
type ContainerStatsSample struct {
Cpu struct {
// number of nanoseconds of CPU time used by the container
// within one second.
Usage uint64 `json:"usage"`
} `json:"cpu"`
Memory struct {
// Units: Bytes.
Usage uint64 `json:"usage"`
} `json:"memory"`
}
// This is not exported. // This is not exported.
// Use FillPercentile to calculate percentiles // Use FillPercentile to calculate percentiles
type percentile struct { type percentile struct {
@ -151,11 +175,37 @@ type percentile struct {
type ContainerStatsSummary struct { type ContainerStatsSummary struct {
// TODO(dengnan): More things? // TODO(dengnan): More things?
MaxMemoryUsage uint64 `json:"max_memory_usage,omitempty"` MaxMemoryUsage uint64 `json:"max_memory_usage,omitempty"`
AvgMemoryUsage uint64 `json:"avg_memory_usage,omitempty"` AvgMemoryUsage uint64 `json:"avg_memory_usage,omitempty"`
Samples []*ContainerStats `json:"samples,omitempty"` Samples []*ContainerStatsSample `json:"samples,omitempty"`
MemoryUsagePercentiles []percentile `json:"memory_usage_percentiles,omitempty"` MemoryUsagePercentiles []percentile `json:"memory_usage_percentiles,omitempty"`
CpuUsagePercentiles []percentile `json:"cpu_usage_percentiles,omitempty"` CpuUsagePercentiles []percentile `json:"cpu_usage_percentiles,omitempty"`
}
// Each sample needs two stats because the cpu usage in ContainerStats is
// cumulative.
// prev should be an earlier observation than current.
// This method is not thread/goroutine safe.
func (self *ContainerStatsSummary) AddSample(prev, current *ContainerStats) {
if prev == nil || current == nil {
return
}
// Ignore this sample if it is incomplete
if prev.Cpu == nil || prev.Memory == nil || current.Cpu == nil || current.Memory == nil {
return
}
// prev must be an early observation
if !current.Timestamp.After(prev.Timestamp) {
return
}
sample := new(ContainerStatsSample)
// Caculate the diff to get the CPU usage within the time interval.
sample.Cpu.Usage = current.Cpu.Usage.Total - prev.Cpu.Usage.Total
// Memory usage is current memory usage
sample.Memory.Usage = current.Memory.Usage
self.Samples = append(self.Samples, sample)
return
} }
type uint64Slice []uint64 type uint64Slice []uint64
@ -190,7 +240,7 @@ func (self uint64Slice) Percentiles(ps ...int) []uint64 {
} }
// len(bs) <= len(as) // len(bs) <= len(as)
func float64Zipuint64(as []int, bs []uint64) []percentile { func intZipuint64(as []int, bs []uint64) []percentile {
if len(bs) == 0 { if len(bs) == 0 {
return nil return nil
} }
@ -216,16 +266,12 @@ func (self *ContainerStatsSummary) FillPercentiles(cpuPercentages, memoryPercent
if sample == nil { if sample == nil {
continue continue
} }
if sample.Cpu != nil { cpuUsages = append(cpuUsages, sample.Cpu.Usage)
cpuUsages = append(cpuUsages, sample.Cpu.Usage.Total) memUsages = append(memUsages, sample.Memory.Usage)
}
if sample.Memory != nil {
memUsages = append(memUsages, sample.Memory.Usage)
}
} }
cpuPercentiles := uint64Slice(cpuUsages).Percentiles(cpuPercentages...) cpuPercentiles := uint64Slice(cpuUsages).Percentiles(cpuPercentages...)
memPercentiles := uint64Slice(memUsages).Percentiles(memoryPercentages...) memPercentiles := uint64Slice(memUsages).Percentiles(memoryPercentages...)
self.CpuUsagePercentiles = float64Zipuint64(cpuPercentages, cpuPercentiles) self.CpuUsagePercentiles = intZipuint64(cpuPercentages, cpuPercentiles)
self.MemoryUsagePercentiles = float64Zipuint64(memoryPercentages, memPercentiles) self.MemoryUsagePercentiles = intZipuint64(memoryPercentages, memPercentiles)
} }

View File

@ -70,17 +70,17 @@ func TestPercentiles(t *testing.T) {
for i := 0; i < N; i++ { for i := 0; i < N; i++ {
data[i] = uint64(i) data[i] = uint64(i)
} }
ps := []float64{ ps := []int{
0.8, 80,
0.9, 90,
0.5, 50,
} }
ss := uint64Slice(data).Percentiles(ps...) ss := uint64Slice(data).Percentiles(ps...)
for i, s := range ss { for i, s := range ss {
p := ps[i] p := ps[i]
d := uint64(float64(N) * p) d := uint64(float64(N) * (float64(p) / 100.0))
if d != s { if d != s {
t.Errorf("%v \\%tile data should be %v, but got %v", p*float64(100), d, s) t.Errorf("%v \\%tile data should be %v, but got %v", float64(p)/100.0, d, s)
} }
} }
} }