stringutil_test.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package stringutil_test
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "strings"
  6. "sync"
  7. "testing"
  8. "time"
  9. "github.com/opencost/opencost/core/pkg/util/stringutil"
  10. )
  11. var oldBank sync.Map
  12. type bankTest struct {
  13. Bank func(string) string
  14. BankFunc func(string, func() string) string
  15. Clear func()
  16. }
  17. var (
  18. legacyTest = bankTest{
  19. Bank: BankLegacy,
  20. BankFunc: func(s string, f func() string) string { return s },
  21. Clear: ClearBankLegacy,
  22. }
  23. standardBankTest = bankTest{
  24. Bank: stringutil.Bank,
  25. BankFunc: stringutil.BankFunc,
  26. Clear: stringutil.ClearBank,
  27. }
  28. )
  29. // This is the old implementation of the string bank to use for comparison benchmarks
  30. func BankLegacy(s string) string {
  31. ss, _ := oldBank.LoadOrStore(s, s)
  32. return ss.(string)
  33. }
  34. func ClearBankLegacy() {
  35. oldBank = sync.Map{}
  36. }
  37. func copyString(s string) string {
  38. return string([]byte(s))
  39. }
  40. func generateBenchData(totalStrings, totalUnique int) []string {
  41. randStrings := make([]string, 0, totalStrings)
  42. r := rand.New(rand.NewSource(27644437))
  43. // create totalUnique unique strings
  44. for range totalUnique {
  45. randStrings = append(
  46. randStrings,
  47. fmt.Sprintf("%s/%s/%s", stringutil.RandSeqWith(r, 10), stringutil.RandSeqWith(r, 10), stringutil.RandSeqWith(r, 10)),
  48. )
  49. }
  50. // set the seed such that the resulting "remainder" strings are deterministic for each bench
  51. r = rand.New(rand.NewSource(1523942))
  52. // append a random selection from 0-totalUnique to the list.
  53. for range totalStrings - totalUnique {
  54. randStrings = append(randStrings, strings.Clone(randStrings[r.Intn(totalUnique)]))
  55. }
  56. // shuffle the list of strings
  57. r.Shuffle(totalStrings, func(i, j int) { randStrings[i], randStrings[j] = randStrings[j], randStrings[i] })
  58. return randStrings
  59. }
  60. func benchmarkStringBank(b *testing.B, bt bankTest, totalStrings, totalUnique int, useBankFunc bool) {
  61. b.StopTimer()
  62. randStrings := generateBenchData(totalStrings, totalUnique)
  63. b.Run(b.Name(), func(b *testing.B) {
  64. for i := 0; i < b.N; i++ {
  65. b.StartTimer()
  66. for bb := 0; bb < totalStrings; bb++ {
  67. if useBankFunc {
  68. bt.BankFunc(randStrings[bb], func() string { return randStrings[bb] })
  69. } else {
  70. bt.Bank(randStrings[bb])
  71. }
  72. }
  73. b.StopTimer()
  74. bt.Clear()
  75. //runtime.GC()
  76. //debug.FreeOSMemory()
  77. }
  78. })
  79. }
  80. func BenchmarkLegacyStringBank90PercentDuplicate(b *testing.B) {
  81. benchmarkStringBank(b, legacyTest, 1_000_000, 100_000, false)
  82. }
  83. func BenchmarkLegacyStringBank75PercentDuplicate(b *testing.B) {
  84. benchmarkStringBank(b, legacyTest, 1_000_000, 250_000, false)
  85. }
  86. func BenchmarkLegacyStringBank50PercentDuplicate(b *testing.B) {
  87. benchmarkStringBank(b, legacyTest, 1_000_000, 100_000, false)
  88. }
  89. func BenchmarkLegacyStringBank25PercentDuplicate(b *testing.B) {
  90. benchmarkStringBank(b, legacyTest, 1_000_000, 750_000, false)
  91. }
  92. func BenchmarkLegacyStringBankNoDuplicate(b *testing.B) {
  93. benchmarkStringBank(b, legacyTest, 1_000_000, 1_000_000, false)
  94. }
  95. func BenchmarkStringBank90PercentDuplicate(b *testing.B) {
  96. benchmarkStringBank(b, standardBankTest, 1_000_000, 100_000, false)
  97. }
  98. func BenchmarkStringBank75PercentDuplicate(b *testing.B) {
  99. benchmarkStringBank(b, standardBankTest, 1_000_000, 250_000, false)
  100. }
  101. func BenchmarkStringBank50PercentDuplicate(b *testing.B) {
  102. benchmarkStringBank(b, standardBankTest, 1_000_000, 100_000, false)
  103. }
  104. func BenchmarkStringBank25PercentDuplicate(b *testing.B) {
  105. benchmarkStringBank(b, standardBankTest, 1_000_000, 750_000, false)
  106. }
  107. func BenchmarkStringBankNoDuplicate(b *testing.B) {
  108. benchmarkStringBank(b, standardBankTest, 1_000_000, 1_000_000, false)
  109. }
  110. func BenchmarkStringBankFunc90PercentDuplicate(b *testing.B) {
  111. benchmarkStringBank(b, standardBankTest, 1_000_000, 100_000, true)
  112. }
  113. func BenchmarkStringBankFunc75PercentDuplicate(b *testing.B) {
  114. benchmarkStringBank(b, standardBankTest, 1_000_000, 250_000, true)
  115. }
  116. func BenchmarkStringBankFunc50PercentDuplicate(b *testing.B) {
  117. benchmarkStringBank(b, standardBankTest, 1_000_000, 100_000, true)
  118. }
  119. func BenchmarkStringBankFunc25PercentDuplicate(b *testing.B) {
  120. benchmarkStringBank(b, standardBankTest, 1_000_000, 750_000, true)
  121. }
  122. func BenchmarkStringBankFuncNoDuplicate(b *testing.B) {
  123. benchmarkStringBank(b, standardBankTest, 1_000_000, 1_000_000, true)
  124. }
  125. const LruCapacity = 500_000
  126. const LruEvictInterval = 5 * time.Second
  127. func BenchmarkLruStringBankFunc90PercentDuplicate(b *testing.B) {
  128. sb := stringutil.NewLruStringBank(LruCapacity, LruEvictInterval)
  129. defer func() {
  130. if lruBank, ok := sb.(interface{ Stop() }); ok {
  131. lruBank.Stop()
  132. }
  133. }()
  134. stringutil.UpdateStringBank(sb)
  135. benchmarkStringBank(b, standardBankTest, 1_000_000, 100_000, true)
  136. }
  137. func BenchmarkLruStringBankFunc75PercentDuplicate(b *testing.B) {
  138. sb := stringutil.NewLruStringBank(LruCapacity, LruEvictInterval)
  139. defer func() {
  140. if lruBank, ok := sb.(interface{ Stop() }); ok {
  141. lruBank.Stop()
  142. }
  143. }()
  144. stringutil.UpdateStringBank(sb)
  145. benchmarkStringBank(b, standardBankTest, 1_000_000, 250_000, true)
  146. }
  147. func BenchmarkLruStringBankFunc50PercentDuplicate(b *testing.B) {
  148. sb := stringutil.NewLruStringBank(LruCapacity, LruEvictInterval)
  149. defer func() {
  150. if lruBank, ok := sb.(interface{ Stop() }); ok {
  151. lruBank.Stop()
  152. }
  153. }()
  154. stringutil.UpdateStringBank(sb)
  155. benchmarkStringBank(b, standardBankTest, 1_000_000, 100_000, true)
  156. }
  157. func BenchmarkLruStringBankFunc25PercentDuplicate(b *testing.B) {
  158. sb := stringutil.NewLruStringBank(LruCapacity, LruEvictInterval)
  159. defer func() {
  160. if lruBank, ok := sb.(interface{ Stop() }); ok {
  161. lruBank.Stop()
  162. }
  163. }()
  164. stringutil.UpdateStringBank(sb)
  165. benchmarkStringBank(b, standardBankTest, 1_000_000, 750_000, true)
  166. }
  167. func BenchmarkLruStringBankFuncNoDuplicate(b *testing.B) {
  168. sb := stringutil.NewLruStringBank(LruCapacity, LruEvictInterval)
  169. defer func() {
  170. if lruBank, ok := sb.(interface{ Stop() }); ok {
  171. lruBank.Stop()
  172. }
  173. }()
  174. stringutil.UpdateStringBank(sb)
  175. benchmarkStringBank(b, standardBankTest, 1_000_000, 1_000_000, true)
  176. }