scheduler_test.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package nebula
  2. import (
  3. "testing"
  4. "time"
  5. )
  6. func TestScheduler_PooledReuse(t *testing.T) {
  7. ctx := t.Context()
  8. s := NewScheduler[int](16)
  9. delivered := make(chan int, 256)
  10. go s.Run(ctx, func(item int) { delivered <- item })
  11. const N = 100
  12. for i := range N {
  13. s.Schedule(ctx, i, time.Millisecond)
  14. }
  15. deadline := time.After(2 * time.Second)
  16. got := 0
  17. for got < N {
  18. select {
  19. case <-delivered:
  20. got++
  21. case <-deadline:
  22. t.Fatalf("only %d/%d items delivered", got, N)
  23. }
  24. }
  25. }
  26. // BenchmarkScheduler_Schedule reports allocations per Schedule call.
  27. // In steady state the Scheduler's sync.Pool means we should see zero allocs per op once the pool warms up.
  28. func BenchmarkScheduler_Schedule(b *testing.B) {
  29. ctx := b.Context()
  30. s := NewScheduler[int](b.N)
  31. go s.Run(ctx, func(int) {})
  32. b.ReportAllocs()
  33. b.ResetTimer()
  34. for i := 0; i < b.N; i++ {
  35. s.Schedule(ctx, i, time.Microsecond)
  36. }
  37. }
  38. // BenchmarkBareAfterFunc is the comparison baseline.
  39. // What we'd pay per Schedule if Punchy called time.AfterFunc directly without the pooled Scheduler.
  40. // Allocates a *time.Timer plus a closure each call.
  41. func BenchmarkBareAfterFunc(b *testing.B) {
  42. ctx := b.Context()
  43. queue := make(chan int, b.N)
  44. go func() {
  45. for {
  46. select {
  47. case <-ctx.Done():
  48. return
  49. case <-queue:
  50. }
  51. }
  52. }()
  53. b.ReportAllocs()
  54. b.ResetTimer()
  55. for i := 0; i < b.N; i++ {
  56. i := i
  57. time.AfterFunc(time.Microsecond, func() {
  58. select {
  59. case queue <- i:
  60. case <-ctx.Done():
  61. }
  62. })
  63. }
  64. }