1
// Copyright 2014 The Prometheus Authors
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at
6
// http://www.apache.org/licenses/LICENSE-2.0
8
// Unless required by applicable law or agreed to in writing, software
9
// distributed under the License is distributed on an "AS IS" BASIS,
10
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
// See the License for the specific language governing permissions and
12
// limitations under the License.
25
dto "github.com/prometheus/client_model/go"
28
func benchmarkSummaryObserve(w int, b *testing.B) {
31
wg := new(sync.WaitGroup)
34
g := new(sync.WaitGroup)
37
s := NewSummary(SummaryOpts{})
39
for i := 0; i < w; i++ {
43
for i := 0; i < b.N; i++ {
56
func BenchmarkSummaryObserve1(b *testing.B) {
57
benchmarkSummaryObserve(1, b)
60
func BenchmarkSummaryObserve2(b *testing.B) {
61
benchmarkSummaryObserve(2, b)
64
func BenchmarkSummaryObserve4(b *testing.B) {
65
benchmarkSummaryObserve(4, b)
68
func BenchmarkSummaryObserve8(b *testing.B) {
69
benchmarkSummaryObserve(8, b)
72
func benchmarkSummaryWrite(w int, b *testing.B) {
75
wg := new(sync.WaitGroup)
78
g := new(sync.WaitGroup)
81
s := NewSummary(SummaryOpts{})
83
for i := 0; i < 1000000; i++ {
87
for j := 0; j < w; j++ {
88
outs := make([]dto.Metric, b.N)
90
go func(o []dto.Metric) {
93
for i := 0; i < b.N; i++ {
106
func BenchmarkSummaryWrite1(b *testing.B) {
107
benchmarkSummaryWrite(1, b)
110
func BenchmarkSummaryWrite2(b *testing.B) {
111
benchmarkSummaryWrite(2, b)
114
func BenchmarkSummaryWrite4(b *testing.B) {
115
benchmarkSummaryWrite(4, b)
118
func BenchmarkSummaryWrite8(b *testing.B) {
119
benchmarkSummaryWrite(8, b)
122
func TestSummaryConcurrency(t *testing.T) {
124
t.Skip("Skipping test in short mode.")
129
it := func(n uint32) bool {
130
mutations := int(n%1e4 + 1e4)
131
concLevel := int(n%5 + 1)
132
total := mutations * concLevel
134
var start, end sync.WaitGroup
138
sum := NewSummary(SummaryOpts{
139
Name: "test_summary",
143
allVars := make([]float64, total)
144
var sampleSum float64
145
for i := 0; i < concLevel; i++ {
146
vals := make([]float64, mutations)
147
for j := 0; j < mutations; j++ {
148
v := rand.NormFloat64()
150
allVars[i*mutations+j] = v
154
go func(vals []float64) {
156
for _, v := range vals {
162
sort.Float64s(allVars)
168
if got, want := int(*m.Summary.SampleCount), total; got != want {
169
t.Errorf("got sample count %d, want %d", got, want)
171
if got, want := *m.Summary.SampleSum, sampleSum; math.Abs((got-want)/want) > 0.001 {
172
t.Errorf("got sample sum %f, want %f", got, want)
175
objectives := make([]float64, 0, len(DefObjectives))
176
for qu := range DefObjectives {
177
objectives = append(objectives, qu)
179
sort.Float64s(objectives)
181
for i, wantQ := range objectives {
182
ε := DefObjectives[wantQ]
183
gotQ := *m.Summary.Quantile[i].Quantile
184
gotV := *m.Summary.Quantile[i].Value
185
min, max := getBounds(allVars, wantQ, ε)
187
t.Errorf("got quantile %f, want %f", gotQ, wantQ)
189
if gotV < min || gotV > max {
190
t.Errorf("got %f for quantile %f, want [%f,%f]", gotV, gotQ, min, max)
196
if err := quick.Check(it, nil); err != nil {
201
func TestSummaryVecConcurrency(t *testing.T) {
203
t.Skip("Skipping test in short mode.")
208
objectives := make([]float64, 0, len(DefObjectives))
209
for qu := range DefObjectives {
211
objectives = append(objectives, qu)
213
sort.Float64s(objectives)
215
it := func(n uint32) bool {
216
mutations := int(n%1e4 + 1e4)
217
concLevel := int(n%7 + 1)
218
vecLength := int(n%3 + 1)
220
var start, end sync.WaitGroup
224
sum := NewSummaryVec(
226
Name: "test_summary",
232
allVars := make([][]float64, vecLength)
233
sampleSums := make([]float64, vecLength)
234
for i := 0; i < concLevel; i++ {
235
vals := make([]float64, mutations)
236
picks := make([]int, mutations)
237
for j := 0; j < mutations; j++ {
238
v := rand.NormFloat64()
240
pick := rand.Intn(vecLength)
242
allVars[pick] = append(allVars[pick], v)
243
sampleSums[pick] += v
246
go func(vals []float64) {
248
for i, v := range vals {
249
sum.WithLabelValues(string('A' + picks[i])).Observe(v)
254
for _, vars := range allVars {
260
for i := 0; i < vecLength; i++ {
262
s := sum.WithLabelValues(string('A' + i))
264
if got, want := int(*m.Summary.SampleCount), len(allVars[i]); got != want {
265
t.Errorf("got sample count %d for label %c, want %d", got, 'A'+i, want)
267
if got, want := *m.Summary.SampleSum, sampleSums[i]; math.Abs((got-want)/want) > 0.001 {
268
t.Errorf("got sample sum %f for label %c, want %f", got, 'A'+i, want)
270
for j, wantQ := range objectives {
271
ε := DefObjectives[wantQ]
272
gotQ := *m.Summary.Quantile[j].Quantile
273
gotV := *m.Summary.Quantile[j].Value
274
min, max := getBounds(allVars[i], wantQ, ε)
276
t.Errorf("got quantile %f for label %c, want %f", gotQ, 'A'+i, wantQ)
278
if gotV < min || gotV > max {
279
t.Errorf("got %f for quantile %f for label %c, want [%f,%f]", gotV, gotQ, 'A'+i, min, max)
286
if err := quick.Check(it, nil); err != nil {
291
func TestSummaryDecay(t *testing.T) {
293
t.Skip("Skipping test in short mode.")
294
// More because it depends on timing than because it is particularly long...
297
sum := NewSummary(SummaryOpts{
298
Name: "test_summary",
300
MaxAge: 100 * time.Millisecond,
301
Objectives: map[float64]float64{0.1: 0.001},
307
tick := time.NewTicker(time.Millisecond)
308
for _ = range tick.C {
310
sum.Observe(float64(i))
313
if got, want := *m.Summary.Quantile[0].Value, math.Max(float64(i)/10, float64(i-90)); math.Abs(got-want) > 20 {
314
t.Errorf("%d. got %f, want %f", i, got, want)
323
// Wait for MaxAge without observations and make sure quantiles are NaN.
324
time.Sleep(100 * time.Millisecond)
326
if got := *m.Summary.Quantile[0].Value; !math.IsNaN(got) {
327
t.Errorf("got %f, want NaN after expiration", got)
331
func getBounds(vars []float64, q, ε float64) (min, max float64) {
332
// TODO(beorn7): This currently tolerates an error of up to 2*ε. The
333
// error must be at most ε, but for some reason, it's sometimes slightly
334
// higher. That's a bug.
335
n := float64(len(vars))
336
lower := int((q - 2*ε) * n)
337
upper := int(math.Ceil((q + 2*ε) * n))
342
max = vars[len(vars)-1]
343
if upper < len(vars) {