1
0

performance_test.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. package utils
  2. import (
  3. "fmt"
  4. "sync"
  5. "testing"
  6. "time"
  7. )
  8. func TestStringPool(t *testing.T) {
  9. pool := NewStringPool()
  10. // Test basic functionality
  11. buf := pool.Get()
  12. if len(buf) != 0 {
  13. t.Errorf("Expected empty buffer, got length %d", len(buf))
  14. }
  15. buf = append(buf, []byte("test")...)
  16. pool.Put(buf)
  17. // Test string interning
  18. s1 := "test_string"
  19. s2 := "test_string"
  20. interned1 := pool.Intern(s1)
  21. interned2 := pool.Intern(s2)
  22. if interned1 != interned2 {
  23. t.Error("Expected same interned strings")
  24. }
  25. // Test size and clear
  26. if pool.Size() == 0 {
  27. t.Error("Expected non-zero pool size")
  28. }
  29. pool.Clear()
  30. if pool.Size() != 0 {
  31. t.Error("Expected zero pool size after clear")
  32. }
  33. }
  34. func TestMemoryPool(t *testing.T) {
  35. pool := NewMemoryPool()
  36. // Test getting different sizes
  37. buf1 := pool.Get(100)
  38. if cap(buf1) < 100 {
  39. t.Errorf("Expected capacity >= 100, got %d", cap(buf1))
  40. }
  41. buf2 := pool.Get(1000)
  42. if cap(buf2) < 1000 {
  43. t.Errorf("Expected capacity >= 1000, got %d", cap(buf2))
  44. }
  45. // Test putting back
  46. pool.Put(buf1)
  47. pool.Put(buf2)
  48. // Test very large buffer (should allocate directly)
  49. largeBuf := pool.Get(100000)
  50. if cap(largeBuf) < 100000 {
  51. t.Errorf("Expected capacity >= 100000, got %d", cap(largeBuf))
  52. }
  53. }
  54. func TestWorkerPool(t *testing.T) {
  55. numWorkers := 3
  56. queueSize := 10
  57. pool := NewWorkerPool(numWorkers, queueSize)
  58. defer pool.Close()
  59. // Test job submission and execution
  60. var counter int64
  61. var mu sync.Mutex
  62. for i := 0; i < 5; i++ {
  63. success := pool.Submit(func() {
  64. mu.Lock()
  65. counter++
  66. mu.Unlock()
  67. })
  68. if !success {
  69. t.Error("Failed to submit job")
  70. }
  71. }
  72. // Wait for jobs to complete
  73. time.Sleep(100 * time.Millisecond)
  74. mu.Lock()
  75. if counter != 5 {
  76. t.Errorf("Expected counter = 5, got %d", counter)
  77. }
  78. mu.Unlock()
  79. }
  80. func TestBatchProcessor(t *testing.T) {
  81. capacity := 3
  82. bp := NewBatchProcessor(capacity)
  83. // Test adding items
  84. if !bp.Add("item1") {
  85. t.Error("Failed to add item1")
  86. }
  87. if !bp.Add("item2") {
  88. t.Error("Failed to add item2")
  89. }
  90. if !bp.Add("item3") {
  91. t.Error("Failed to add item3")
  92. }
  93. // Should fail to add more than capacity
  94. if bp.Add("item4") {
  95. t.Error("Should have failed to add item4")
  96. }
  97. // Test size
  98. if bp.Size() != 3 {
  99. t.Errorf("Expected size 3, got %d", bp.Size())
  100. }
  101. // Test getting batch
  102. batch := bp.GetBatch()
  103. if len(batch) != 3 {
  104. t.Errorf("Expected batch size 3, got %d", len(batch))
  105. }
  106. // Should be empty after getting batch
  107. if bp.Size() != 0 {
  108. t.Errorf("Expected size 0 after GetBatch, got %d", bp.Size())
  109. }
  110. }
  111. func TestMemoryOptimizer(t *testing.T) {
  112. mo := NewMemoryOptimizer(1024 * 1024) // 1MB threshold
  113. // Test stats retrieval
  114. stats := mo.GetMemoryStats()
  115. if stats == nil {
  116. t.Fatal("Expected non-nil memory stats")
  117. }
  118. if stats.AllocMB < 0 {
  119. t.Error("Expected non-negative allocated memory")
  120. }
  121. // Test check memory usage (should not panic)
  122. mo.CheckMemoryUsage()
  123. }
  124. func TestPerformanceMetrics(t *testing.T) {
  125. pm := NewPerformanceMetrics()
  126. // Record some operations
  127. pm.RecordOperation(10, time.Millisecond*100, true)
  128. pm.RecordOperation(20, time.Millisecond*200, false) // failure
  129. pm.RecordCacheHit()
  130. pm.RecordCacheHit()
  131. pm.RecordCacheMiss()
  132. pm.RecordAllocation(1024)
  133. metrics := pm.GetMetrics()
  134. if metrics["operation_count"] != int64(2) {
  135. t.Errorf("Expected 2 operations, got %v", metrics["operation_count"])
  136. }
  137. if metrics["processed_items"] != int64(30) {
  138. t.Errorf("Expected 30 processed items, got %v", metrics["processed_items"])
  139. }
  140. if metrics["cache_hits"] != int64(2) {
  141. t.Errorf("Expected 2 cache hits, got %v", metrics["cache_hits"])
  142. }
  143. if metrics["cache_misses"] != int64(1) {
  144. t.Errorf("Expected 1 cache miss, got %v", metrics["cache_misses"])
  145. }
  146. cacheHitRate, ok := metrics["cache_hit_rate"].(float64)
  147. if !ok || cacheHitRate < 0.6 || cacheHitRate > 0.7 {
  148. t.Errorf("Expected cache hit rate around 0.67, got %v", cacheHitRate)
  149. }
  150. // Test reset
  151. pm.Reset()
  152. resetMetrics := pm.GetMetrics()
  153. if resetMetrics["operation_count"] != int64(0) {
  154. t.Errorf("Expected 0 operations after reset, got %v", resetMetrics["operation_count"])
  155. }
  156. }
  157. func TestUnsafeConversions(t *testing.T) {
  158. // Test bytes to string conversion
  159. original := []byte("test string")
  160. str := BytesToStringUnsafe(original)
  161. if str != "test string" {
  162. t.Errorf("Expected 'test string', got '%s'", str)
  163. }
  164. // Test string to bytes conversion
  165. originalStr := "test string"
  166. bytes := StringToBytesUnsafe(originalStr)
  167. if string(bytes) != originalStr {
  168. t.Errorf("Expected '%s', got '%s'", originalStr, string(bytes))
  169. }
  170. // Test empty cases
  171. emptyStr := BytesToStringUnsafe(nil)
  172. if emptyStr != "" {
  173. t.Errorf("Expected empty string, got '%s'", emptyStr)
  174. }
  175. emptyBytes := StringToBytesUnsafe("")
  176. if len(emptyBytes) != 0 {
  177. t.Errorf("Expected empty bytes, got length %d", len(emptyBytes))
  178. }
  179. }
  180. func TestAppendInt(t *testing.T) {
  181. testCases := []struct {
  182. input int
  183. expected string
  184. }{
  185. {0, "0"},
  186. {123, "123"},
  187. {-456, "-456"},
  188. {7890, "7890"},
  189. }
  190. for _, tc := range testCases {
  191. buf := make([]byte, 0, 10)
  192. result := AppendInt(buf, tc.input)
  193. if string(result) != tc.expected {
  194. t.Errorf("AppendInt(%d) = '%s', expected '%s'", tc.input, string(result), tc.expected)
  195. }
  196. }
  197. // Test appending to existing buffer
  198. buf := []byte("prefix:")
  199. result := AppendInt(buf, 42)
  200. if string(result) != "prefix:42" {
  201. t.Errorf("Expected 'prefix:42', got '%s'", string(result))
  202. }
  203. }
  204. func BenchmarkStringPool(b *testing.B) {
  205. pool := NewStringPool()
  206. b.ResetTimer()
  207. for i := 0; i < b.N; i++ {
  208. buf := pool.Get()
  209. buf = append(buf, []byte("benchmark test")...)
  210. pool.Put(buf)
  211. }
  212. }
  213. func BenchmarkStringIntern(b *testing.B) {
  214. pool := NewStringPool()
  215. testStrings := []string{
  216. "common_string_1",
  217. "common_string_2",
  218. "common_string_3",
  219. "common_string_1", // duplicate
  220. "common_string_2", // duplicate
  221. }
  222. b.ResetTimer()
  223. for i := 0; i < b.N; i++ {
  224. s := testStrings[i%len(testStrings)]
  225. pool.Intern(s)
  226. }
  227. }
  228. func BenchmarkMemoryPool(b *testing.B) {
  229. pool := NewMemoryPool()
  230. b.ResetTimer()
  231. for i := 0; i < b.N; i++ {
  232. buf := pool.Get(1024)
  233. pool.Put(buf)
  234. }
  235. }
  236. func BenchmarkUnsafeConversions(b *testing.B) {
  237. testBytes := []byte("benchmark test string for conversion")
  238. testString := "benchmark test string for conversion"
  239. b.Run("BytesToStringUnsafe", func(b *testing.B) {
  240. for i := 0; i < b.N; i++ {
  241. _ = BytesToStringUnsafe(testBytes)
  242. }
  243. })
  244. b.Run("StringToBytesUnsafe", func(b *testing.B) {
  245. for i := 0; i < b.N; i++ {
  246. _ = StringToBytesUnsafe(testString)
  247. }
  248. })
  249. b.Run("StandardConversion", func(b *testing.B) {
  250. for i := 0; i < b.N; i++ {
  251. _ = string(testBytes)
  252. }
  253. })
  254. }
  255. func TestStringPoolConcurrency(t *testing.T) {
  256. pool := NewStringPool()
  257. const numGoroutines = 10
  258. const numOperations = 100
  259. var wg sync.WaitGroup
  260. wg.Add(numGoroutines)
  261. for i := 0; i < numGoroutines; i++ {
  262. go func(id int) {
  263. defer wg.Done()
  264. for j := 0; j < numOperations; j++ {
  265. // Test buffer operations
  266. buf := pool.Get()
  267. buf = append(buf, byte(id), byte(j))
  268. pool.Put(buf)
  269. // Test string interning
  270. s := fmt.Sprintf("test_%d_%d", id, j%10) // Limited unique strings
  271. pool.Intern(s)
  272. }
  273. }(i)
  274. }
  275. wg.Wait()
  276. // Pool should have some interned strings
  277. if pool.Size() == 0 {
  278. t.Error("Expected some interned strings after concurrent operations")
  279. }
  280. }