harness_results.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright (C) MongoDB, Inc. 2017-present.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
  6. package benchmark
  7. import (
  8. "fmt"
  9. "time"
  10. "github.com/montanaflynn/stats"
  11. )
  12. type BenchResult struct {
  13. Name string
  14. Trials int
  15. Duration time.Duration
  16. Raw []Result
  17. DataSize int
  18. Operations int
  19. hasErrors *bool
  20. }
  21. func (r *BenchResult) EvergreenPerfFormat() ([]interface{}, error) {
  22. timings := r.timings()
  23. median, err := stats.Median(timings)
  24. if err != nil {
  25. return nil, err
  26. }
  27. min, err := stats.Min(timings)
  28. if err != nil {
  29. return nil, err
  30. }
  31. max, err := stats.Max(timings)
  32. if err != nil {
  33. return nil, err
  34. }
  35. out := []interface{}{
  36. map[string]interface{}{
  37. "name": r.Name + "-throughput",
  38. "results": map[string]interface{}{
  39. "1": map[string]interface{}{
  40. "seconds": r.Duration.Round(time.Millisecond).Seconds(),
  41. "ops_per_second": r.getThroughput(median),
  42. "ops_per_second_values": []float64{
  43. r.getThroughput(min),
  44. r.getThroughput(max),
  45. },
  46. },
  47. },
  48. },
  49. }
  50. if r.DataSize > 0 {
  51. out = append(out, interface{}(map[string]interface{}{
  52. "name": r.Name + "-MB-adjusted",
  53. "results": map[string]interface{}{
  54. "1": map[string]interface{}{
  55. "seconds": r.Duration.Round(time.Millisecond).Seconds(),
  56. "ops_per_second": r.adjustResults(median),
  57. "ops_per_second_values": []float64{
  58. r.adjustResults(min),
  59. r.adjustResults(max),
  60. },
  61. },
  62. },
  63. }))
  64. }
  65. return out, nil
  66. }
  67. func (r *BenchResult) timings() []float64 {
  68. out := []float64{}
  69. for _, r := range r.Raw {
  70. out = append(out, r.Duration.Seconds())
  71. }
  72. return out
  73. }
  74. func (r *BenchResult) totalDuration() time.Duration {
  75. var out time.Duration
  76. for _, trial := range r.Raw {
  77. out += trial.Duration
  78. }
  79. return out
  80. }
  81. func (r *BenchResult) adjustResults(data float64) float64 { return float64(r.DataSize) / data }
  82. func (r *BenchResult) getThroughput(data float64) float64 { return float64(r.Operations) / data }
  83. func (r *BenchResult) roundedRuntime() time.Duration { return roundDurationMS(r.Duration) }
  84. func (r *BenchResult) String() string {
  85. return fmt.Sprintf("name=%s, trials=%d, secs=%s", r.Name, r.Trials, r.Duration)
  86. }
  87. func (r *BenchResult) HasErrors() bool {
  88. if r.hasErrors == nil {
  89. var val bool
  90. for _, res := range r.Raw {
  91. if res.Error != nil {
  92. val = true
  93. break
  94. }
  95. }
  96. r.hasErrors = &val
  97. }
  98. return *r.hasErrors
  99. }
  100. func (r *BenchResult) errReport() []string {
  101. errs := []string{}
  102. for _, res := range r.Raw {
  103. if res.Error != nil {
  104. errs = append(errs, res.Error.Error())
  105. }
  106. }
  107. return errs
  108. }
  109. type Result struct {
  110. Duration time.Duration
  111. Iterations int
  112. Error error
  113. }
  114. func roundDurationMS(d time.Duration) time.Duration {
  115. rounded := d.Round(time.Millisecond)
  116. if rounded == 1<<63-1 {
  117. return 0
  118. }
  119. return rounded
  120. }