func TestEquals(t *testing.T) { h1 := hdrhistogram.New(1, 10000000, 3) for i := 0; i < 1000000; i++ { if err := h1.RecordValue(int64(i)); err != nil { t.Fatal(err) } } h2 := hdrhistogram.New(1, 10000000, 3) for i := 0; i < 10000; i++ { if err := h1.RecordValue(int64(i)); err != nil { t.Fatal(err) } } if h1.Equals(h2) { t.Error("Expected Histograms to not be equivalent") } h1.Reset() h2.Reset() if !h1.Equals(h2) { t.Error("Expected Histograms to be equivalent") } }
// Runf runs the given job at the given concurrency level, at the given rate, // returning a set of results with aggregated latency and throughput // measurements. func (b Bench) Runf(concurrency int, rate float64, job Job) Result { var started, finished sync.WaitGroup started.Add(1) finished.Add(concurrency) result := Result{ Concurrency: concurrency, Latency: hdrhistogram.New(us(b.MinLatency), us(b.MaxLatency), 5), } timings := make(chan *hdrhistogram.Histogram, concurrency) errors := make(chan error, concurrency) workerRate := float64(concurrency) / rate period := time.Duration((workerRate)*1000000) * time.Microsecond for i := 0; i < concurrency; i++ { go func(id int) { defer finished.Done() gen := &Generator{ hist: hdrhistogram.New(us(b.MinLatency), us(b.MaxLatency), 5), success: &result.Success, failure: &result.Failure, period: period, duration: b.Duration, warmup: b.Warmup, } started.Wait() errors <- job(id, gen) timings <- gen.hist }(i) } started.Done() finished.Wait() result.Elapsed = b.Duration close(timings) for v := range timings { result.Latency.Merge(v) } close(errors) for e := range errors { if e != nil { result.Errors = append(result.Errors, e) } } return result }
func TestHighestTrackableValue(t *testing.T) { const maxVal = 11 h := hdrhistogram.New(1, maxVal, 3) if h.HighestTrackableValue() != maxVal { t.Errorf("HighestTrackableValue figures was %v, expected %d", h.HighestTrackableValue(), maxVal) } }
func TestLowestTrackableValue(t *testing.T) { const minVal = 2 h := hdrhistogram.New(minVal, 10, 3) if h.LowestTrackableValue() != minVal { t.Errorf("LowestTrackableValue figures was %v, expected %d", h.LowestTrackableValue(), minVal) } }
func TestByteSize(t *testing.T) { h := hdrhistogram.New(1, 100000, 3) if v, want := h.ByteSize(), 65604; v != want { t.Errorf("ByteSize was %v, but expected %d", v, want) } }
func TestCumulativeDistribution(t *testing.T) { h := hdrhistogram.New(1, 100000000, 3) for i := 0; i < 1000000; i++ { if err := h.RecordValue(int64(i)); err != nil { t.Fatal(err) } } actual := h.CumulativeDistribution() expected := []hdrhistogram.Bracket{ hdrhistogram.Bracket{Quantile: 0, Count: 1, ValueAt: 0}, hdrhistogram.Bracket{Quantile: 50, Count: 500224, ValueAt: 500223}, hdrhistogram.Bracket{Quantile: 75, Count: 750080, ValueAt: 750079}, hdrhistogram.Bracket{Quantile: 87.5, Count: 875008, ValueAt: 875007}, hdrhistogram.Bracket{Quantile: 93.75, Count: 937984, ValueAt: 937983}, hdrhistogram.Bracket{Quantile: 96.875, Count: 969216, ValueAt: 969215}, hdrhistogram.Bracket{Quantile: 98.4375, Count: 984576, ValueAt: 984575}, hdrhistogram.Bracket{Quantile: 99.21875, Count: 992256, ValueAt: 992255}, hdrhistogram.Bracket{Quantile: 99.609375, Count: 996352, ValueAt: 996351}, hdrhistogram.Bracket{Quantile: 99.8046875, Count: 998400, ValueAt: 998399}, hdrhistogram.Bracket{Quantile: 99.90234375, Count: 999424, ValueAt: 999423}, hdrhistogram.Bracket{Quantile: 99.951171875, Count: 999936, ValueAt: 999935}, hdrhistogram.Bracket{Quantile: 99.9755859375, Count: 999936, ValueAt: 999935}, hdrhistogram.Bracket{Quantile: 99.98779296875, Count: 999936, ValueAt: 999935}, hdrhistogram.Bracket{Quantile: 99.993896484375, Count: 1000000, ValueAt: 1000447}, hdrhistogram.Bracket{Quantile: 100, Count: 1000000, ValueAt: 1000447}, } if !reflect.DeepEqual(actual, expected) { t.Errorf("CF was %#v, but expected %#v", actual, expected) } }
func BenchmarkNew(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { hdrhistogram.New(1, 120000, 3) // this could track 1ms-2min } }
func TestSignificantFigures(t *testing.T) { const sigFigs = 4 h := hdrhistogram.New(1, 10, sigFigs) if h.SignificantFigures() != sigFigs { t.Errorf("Significant figures was %v, expected %d", h.SignificantFigures(), sigFigs) } }
// aggregate run contexts func (b *Bench) aggregate() { // aggregate timer metrics for _, n := range b.allContextsKeys(timerKeys) { t := hdrhistogram.New(min, max, precision) b.timers[n] = t for i := 0; i < b.concurrentRuns; i++ { otherTimer, ok := b.runContexts[i].timers[n] if ok { t.Merge(otherTimer) } } } // aggregate counters for _, n := range b.allContextsKeys(counterKeys) { for i := 0; i < b.concurrentRuns; i++ { b.counters[n] += b.runContexts[i].counters[n] } } // aggregate call counts for i := 0; i < b.concurrentRuns; i++ { b.calls += b.runContexts[i].Iteration } }
func TestValueAtQuantile(t *testing.T) { h := hdrhistogram.New(1, 10000000, 3) for i := 0; i < 1000000; i++ { if err := h.RecordValue(int64(i)); err != nil { t.Fatal(err) } } data := []struct { q float64 v int64 }{ {q: 50, v: 500223}, {q: 75, v: 750079}, {q: 90, v: 900095}, {q: 95, v: 950271}, {q: 99, v: 990207}, {q: 99.9, v: 999423}, {q: 99.99, v: 999935}, } for _, d := range data { if v := h.ValueAtQuantile(d.q); v != d.v { t.Errorf("P%v was %v, but expected %v", d.q, v, d.v) } } }
func TestExportImport(t *testing.T) { min := int64(1) max := int64(10000000) sigfigs := 3 h := hdrhistogram.New(min, max, sigfigs) for i := 0; i < 1000000; i++ { if err := h.RecordValue(int64(i)); err != nil { t.Fatal(err) } } s := h.Export() if v := s.LowestTrackableValue; v != min { t.Errorf("LowestTrackableValue was %v, but expected %v", v, min) } if v := s.HighestTrackableValue; v != max { t.Errorf("HighestTrackableValue was %v, but expected %v", v, max) } if v := int(s.SignificantFigures); v != sigfigs { t.Errorf("SignificantFigures was %v, but expected %v", v, sigfigs) } if imported := hdrhistogram.Import(s); !imported.Equals(h) { t.Error("Expected Histograms to be equivalent") } }
// newConnectionBenchmark creates a connectionBenchmark which runs a system // benchmark using the given Requester. The requestRate argument specifies the // number of requests per second to issue. A zero value disables rate limiting // entirely. The duration argument specifies how long to run the benchmark. func newConnectionBenchmark(requester Requester, requestRate uint64, duration time.Duration) *connectionBenchmark { var interval time.Duration if requestRate > 0 { interval = time.Duration(1000000000 / requestRate) } return &connectionBenchmark{ requester: requester, requestRate: requestRate, duration: duration, expectedInterval: interval, successHistogram: hdrhistogram.New(1, maxRecordableLatencyNS, sigFigs), uncorrectedSuccessHistogram: hdrhistogram.New(1, maxRecordableLatencyNS, sigFigs), errorHistogram: hdrhistogram.New(1, maxRecordableLatencyNS, sigFigs), uncorrectedErrorHistogram: hdrhistogram.New(1, maxRecordableLatencyNS, sigFigs), } }
func TestNaN(t *testing.T) { h := hdrhistogram.New(1, 100000, 3) if math.IsNaN(h.Mean()) { t.Error("mean is NaN") } if math.IsNaN(h.StdDev()) { t.Error("stddev is NaN") } }
// NewHistogram initializes a given Histogram. The contained windowed histogram // rotates every 'duration'; both the windowed and the cumulative histogram // track nonnegative values up to 'maxVal' with 'sigFigs' decimal points of // precision. func NewHistogram(metadata Metadata, duration time.Duration, maxVal int64, sigFigs int) *Histogram { dHist := newSlidingHistogram(duration, maxVal, sigFigs) h := &Histogram{ Metadata: metadata, maxVal: maxVal, } h.mu.cumulative = hdrhistogram.New(0, maxVal, sigFigs) h.mu.sliding = dHist return h }
func TestRecordCorrectedValueStall(t *testing.T) { h := hdrhistogram.New(1, 100000, 3) if err := h.RecordCorrectedValue(1000, 100); err != nil { t.Fatal(err) } if v, want := h.ValueAtQuantile(75), int64(800); v != want { t.Errorf("Corrected value was %v, but expected %v", v, want) } }
func TestTotalCount(t *testing.T) { h := hdrhistogram.New(1, 10000000, 3) for i := 0; i < 1000000; i++ { if err := h.RecordValue(int64(i)); err != nil { t.Fatal(err) } if v, want := h.TotalCount(), int64(i+1); v != want { t.Errorf("TotalCount was %v, but expected %v", v, want) } } }
func TestMerge(t *testing.T) { h1 := hdrhistogram.New(1, 1000, 3) h2 := hdrhistogram.New(1, 1000, 3) for i := 0; i < 100; i++ { if err := h1.RecordValue(int64(i)); err != nil { t.Fatal(err) } } for i := 100; i < 200; i++ { if err := h2.RecordValue(int64(i)); err != nil { t.Fatal(err) } } h1.Merge(h2) if v, want := h1.ValueAtQuantile(50), int64(99); v != want { t.Errorf("Median was %v, but expected %v", v, want) } }
func NewHDRHistogram(low, high int64, sigfigs int) (h *HDRHistogram, err error) { defer func() { if msg := recover(); msg != nil { err = fmt.Errorf("%s", msg) } }() return &HDRHistogram{ low: low, high: high, sigfigs: sigfigs, h: hdrhistogram.New(low, high, sigfigs), }, nil }
func TestMax(t *testing.T) { h := hdrhistogram.New(1, 10000000, 3) for i := 0; i < 1000000; i++ { if err := h.RecordValue(int64(i)); err != nil { t.Fatal(err) } } if v, want := h.Max(), int64(999936); v != want { t.Errorf("Max was %v, but expected %v", v, want) } }
func TestStdDev(t *testing.T) { h := hdrhistogram.New(1, 10000000, 3) for i := 0; i < 1000000; i++ { if err := h.RecordValue(int64(i)); err != nil { t.Fatal(err) } } if v, want := h.StdDev(), 288675.1403682715; v != want { t.Errorf("StdDev was %v, but expected %v", v, want) } }
func BenchmarkHistogramRecordValue(b *testing.B) { h := hdrhistogram.New(1, 10000000, 3) for i := 0; i < 1000000; i++ { if err := h.RecordValue(int64(i)); err != nil { b.Fatal(err) } } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { h.RecordValue(100) } }
func TestHighSigFig(t *testing.T) { input := []int64{ 459876, 669187, 711612, 816326, 931423, 1033197, 1131895, 2477317, 3964974, 12718782, } hist := hdrhistogram.New(459876, 12718782, 5) for _, sample := range input { hist.RecordValue(sample) } if v, want := hist.ValueAtQuantile(50), int64(1048575); v != want { t.Errorf("Median was %v, but expected %v", v, want) } }
func TestSubBucketMaskOverflow(t *testing.T) { hist := hdrhistogram.New(2e7, 1e8, 5) for _, sample := range [...]int64{1e8, 2e7, 3e7} { hist.RecordValue(sample) } for q, want := range map[float64]int64{ 50: 33554431, 83.33: 33554431, 83.34: 100663295, 99: 100663295, } { if got := hist.ValueAtQuantile(q); got != want { t.Errorf("got %d for %fth percentile. want: %d", got, q, want) } } }
func (s *subscriber) start() { latencies := hdrhistogram.New(0, maxRecordableLatencyMS, sigFigs) for { message, err := s.Recv() now := time.Now().UnixNano() if err != nil { log.Printf("Subscriber error: %s", err.Error()) s.mu.Lock() s.results = &result{Err: err.Error()} s.mu.Unlock() return } then, _ := binary.Varint(message) latencies.RecordValue((now - then) / 1000) if !s.hasStarted { s.hasStarted = true s.started = time.Now().UnixNano() } s.counter++ if s.counter == s.numMessages { s.stopped = time.Now().UnixNano() durationMS := float32(s.stopped-s.started) / 1000000.0 s.mu.Lock() s.results = &result{ Duration: durationMS, Throughput: 1000 * float32(s.numMessages) / durationMS, Latency: &latencyResults{ Min: latencies.Min(), Q1: latencies.ValueAtQuantile(25), Q2: latencies.ValueAtQuantile(50), Q3: latencies.ValueAtQuantile(75), Max: latencies.Max(), Mean: latencies.Mean(), StdDev: latencies.StdDev(), }, } s.mu.Unlock() log.Println("Subscriber completed") return } } }
func init() { stats.hist = hdrhistogram.New(0, 0x7fffffff, 1) stats.start = time.Now() stats.opCounts = map[int]int{} // Compute the total of all op relative frequencies. var relFreqTotal float64 for _, op := range ops { relFreqTotal += op.relFreq } // Normalize frequencies. var normFreqTotal float64 for _, op := range ops { normFreq := op.relFreq / relFreqTotal op.normFreq = normFreqTotal + normFreq normFreqTotal += normFreq } }
func TestDistribution(t *testing.T) { h := hdrhistogram.New(8, 1024, 3) for i := 0; i < 1024; i++ { if err := h.RecordValue(int64(i)); err != nil { t.Fatal(err) } } actual := h.Distribution() if len(actual) != 128 { t.Errorf("Number of bars seen was %v, expected was 128", len(actual)) } for _, b := range actual { if b.Count != 8 { t.Errorf("Count per bar seen was %v, expected was 8", b.Count) } } }
func NewOneMeasurementHdrHistogram(name string, props Properties) (*OneMeasurementHdrHistogram, error) { prop := props.GetDefault(PropertyPercentiles, PropertyPercentilesDefault) percentiles := parsePercentileValues(prop, PropertyPercentilesDefault) prop = props.GetDefault(PropertyHdrHistogramOutput, PropertyHdrHistogramOutputDefault) shouldLog, err := strconv.ParseBool(prop) if err != nil { return nil, err } prop = props.GetDefault(PropertyHdrHistogramMax, PropertyHdrHistogramMaxDefault) max, err := strconv.ParseInt(prop, 0, 64) if err != nil { return nil, err } prop = props.GetDefault(PropertyHdrHistogramSig, PropertyHdrHistogramSigDefault) sig, err := strconv.ParseInt(prop, 0, 64) if err != nil { return nil, err } var filePath string var f *os.File var writer *HdrHistogramLogWriter if !shouldLog { filePath = "" f = nil writer = nil } else { filePath = props.GetDefault(PropertyHdrHistogramOutputPath, PropertyHdrHistogramOutputPathDefault) f, err = os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { return nil, err } writer = NewHdrHistogramLogWriter(f) } object := &OneMeasurementHdrHistogram{ OneMeasurementBase: NewOneMeasurementBase(name), histogram: hdrhistogram.New(0, max, int(sig)), filePath: filePath, file: f, writer: writer, percentiles: percentiles, } return object, nil }
func (s *Sampler) Init() { s.outOfRangeErrors = 0 s.histogram = hdrhistogram.New(s.Min, s.Max, s.Sigint) // we need to know the bucket Quantiles, for this we just add every possible value to the histogram.. for i := s.Min; i < s.Max; i++ { s.histogram.RecordValue(i) } //We then extract hdrhistograms own bucket range distribution := s.histogram.CumulativeDistribution() s.bucketQuantiles = make([]float64, 0, len(distribution)) //and save it for later use for _, d := range distribution { s.bucketQuantiles = append(s.bucketQuantiles, d.Quantile) } s.histogram.Reset() // Pipe stuff // Check if pipe already exists pipeExists := false fileInfo, err := os.Stat(s.Path) if err == nil { if (fileInfo.Mode() & os.ModeNamedPipe) > 0 { pipeExists = true } else { fmt.Printf("%d != %d\n", os.ModeNamedPipe, fileInfo.Mode()) panic(s.Path + " exists, but it's not a named pipe (FIFO)") } } // Try to create pipe if needed if !pipeExists { err := syscall.Mkfifo(s.Path, 0666) if err != nil { panic(err.Error()) } } }
// AssertCirconusNormalHistogram ensures the Circonus Histogram data captured in // the result slice abides a normal distribution. func AssertCirconusNormalHistogram(t *testing.T, mean, stdev, min, max int64, result []string) { if len(result) <= 0 { t.Fatal("no results") } // Circonus just dumps the raw counts. We need to do our own statistical analysis. h := hdrhistogram.New(min, max, 3) for _, s := range result { // "H[1.23e04]=123" toks := strings.Split(s, "=") if len(toks) != 2 { t.Fatalf("bad H value: %q", s) } var bucket string bucket = toks[0] bucket = bucket[2 : len(bucket)-1] // "H[1.23e04]" -> "1.23e04" f, err := strconv.ParseFloat(bucket, 64) if err != nil { t.Fatalf("error parsing H value: %q: %v", s, err) } count, err := strconv.ParseFloat(toks[1], 64) if err != nil { t.Fatalf("error parsing H count: %q: %v", s, err) } h.RecordValues(int64(f), int64(count)) } // Apparently Circonus buckets observations by dropping a sigfig, so we have // very coarse tolerance. var tolerance int64 = 30 for _, quantile := range []int{50, 90, 99} { want := normalValueAtQuantile(mean, stdev, quantile) have := h.ValueAtQuantile(float64(quantile)) if int64(math.Abs(float64(want)-float64(have))) > tolerance { t.Errorf("quantile %d: want %d, have %d", quantile, want, have) } } }
// aggregate run contexts func (b *Bench) aggregate() { // aggregate timer metrics for n := range b.runContexts[0].timers { t := hdrhistogram.New(min, max, precision) b.timers[n] = t for i := 0; i < b.concurrentRuns; i++ { t.Merge(b.runContexts[i].timers[n]) } } // aggregate counters for n := range b.runContexts[0].counters { for i := 0; i < b.concurrentRuns; i++ { b.counters[n] += b.runContexts[i].counters[n] } } // aggregate call counts for i := 0; i < b.concurrentRuns; i++ { b.calls += b.runContexts[i].Iteration } }