wrr_test.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. *
  3. * Copyright 2019 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package wrr
  18. import (
  19. "errors"
  20. "math"
  21. "math/rand"
  22. "strconv"
  23. "testing"
  24. "github.com/google/go-cmp/cmp"
  25. "google.golang.org/grpc/internal/grpctest"
  26. )
  27. type s struct {
  28. grpctest.Tester
  29. }
  30. func Test(t *testing.T) {
  31. grpctest.RunSubTests(t, s{})
  32. }
  33. const iterCount = 10000
  34. func equalApproximate(a, b float64) error {
  35. opt := cmp.Comparer(func(x, y float64) bool {
  36. delta := math.Abs(x - y)
  37. mean := math.Abs(x+y) / 2.0
  38. return delta/mean < 0.05
  39. })
  40. if !cmp.Equal(a, b, opt) {
  41. return errors.New(cmp.Diff(a, b))
  42. }
  43. return nil
  44. }
  45. func testWRRNext(t *testing.T, newWRR func() WRR) {
  46. tests := []struct {
  47. name string
  48. weights []int64
  49. }{
  50. {
  51. name: "1-1-1",
  52. weights: []int64{1, 1, 1},
  53. },
  54. {
  55. name: "1-2-3",
  56. weights: []int64{1, 2, 3},
  57. },
  58. {
  59. name: "5-3-2",
  60. weights: []int64{5, 3, 2},
  61. },
  62. {
  63. name: "17-23-37",
  64. weights: []int64{17, 23, 37},
  65. },
  66. {
  67. name: "no items",
  68. weights: []int64{},
  69. },
  70. }
  71. for _, tt := range tests {
  72. t.Run(tt.name, func(t *testing.T) {
  73. w := newWRR()
  74. if len(tt.weights) == 0 {
  75. if next := w.Next(); next != nil {
  76. t.Fatalf("w.Next returns non nil value:%v when there is no item", next)
  77. }
  78. return
  79. }
  80. var sumOfWeights int64
  81. for i, weight := range tt.weights {
  82. w.Add(i, weight)
  83. sumOfWeights += weight
  84. }
  85. results := make(map[int]int)
  86. for i := 0; i < iterCount; i++ {
  87. results[w.Next().(int)]++
  88. }
  89. wantRatio := make([]float64, len(tt.weights))
  90. for i, weight := range tt.weights {
  91. wantRatio[i] = float64(weight) / float64(sumOfWeights)
  92. }
  93. gotRatio := make([]float64, len(tt.weights))
  94. for i, count := range results {
  95. gotRatio[i] = float64(count) / iterCount
  96. }
  97. for i := range wantRatio {
  98. if err := equalApproximate(gotRatio[i], wantRatio[i]); err != nil {
  99. t.Errorf("%v not equal %v", i, err)
  100. }
  101. }
  102. })
  103. }
  104. }
  105. func (s) TestRandomWRRNext(t *testing.T) {
  106. testWRRNext(t, NewRandom)
  107. }
  108. func (s) TestEdfWrrNext(t *testing.T) {
  109. testWRRNext(t, NewEDF)
  110. }
  111. func BenchmarkRandomWRRNext(b *testing.B) {
  112. for _, n := range []int{100, 500, 1000} {
  113. b.Run("equal-weights-"+strconv.Itoa(n)+"-items", func(b *testing.B) {
  114. w := NewRandom()
  115. sumOfWeights := n
  116. for i := 0; i < n; i++ {
  117. w.Add(i, 1)
  118. }
  119. b.ResetTimer()
  120. for i := 0; i < b.N; i++ {
  121. for i := 0; i < sumOfWeights; i++ {
  122. w.Next()
  123. }
  124. }
  125. })
  126. }
  127. var maxWeight int64 = 1024
  128. for _, n := range []int{100, 500, 1000} {
  129. b.Run("random-weights-"+strconv.Itoa(n)+"-items", func(b *testing.B) {
  130. w := NewRandom()
  131. var sumOfWeights int64
  132. for i := 0; i < n; i++ {
  133. weight := rand.Int63n(maxWeight + 1)
  134. w.Add(i, weight)
  135. sumOfWeights += weight
  136. }
  137. b.ResetTimer()
  138. for i := 0; i < b.N; i++ {
  139. for i := 0; i < int(sumOfWeights); i++ {
  140. w.Next()
  141. }
  142. }
  143. })
  144. }
  145. itemsNum := 200
  146. heavyWeight := int64(itemsNum)
  147. lightWeight := int64(1)
  148. heavyIndices := []int{0, itemsNum / 2, itemsNum - 1}
  149. for _, heavyIndex := range heavyIndices {
  150. b.Run("skew-weights-heavy-index-"+strconv.Itoa(heavyIndex), func(b *testing.B) {
  151. w := NewRandom()
  152. var sumOfWeights int64
  153. for i := 0; i < itemsNum; i++ {
  154. var weight int64
  155. if i == heavyIndex {
  156. weight = heavyWeight
  157. } else {
  158. weight = lightWeight
  159. }
  160. sumOfWeights += weight
  161. w.Add(i, weight)
  162. }
  163. b.ResetTimer()
  164. for i := 0; i < b.N; i++ {
  165. for i := 0; i < int(sumOfWeights); i++ {
  166. w.Next()
  167. }
  168. }
  169. })
  170. }
  171. }
  172. func init() {
  173. r := rand.New(rand.NewSource(0))
  174. grpcrandInt63n = r.Int63n
  175. }