|
9 | 9 | "net/netip"
|
10 | 10 | "os"
|
11 | 11 | "path/filepath"
|
| 12 | + "sync" |
12 | 13 | "testing"
|
13 | 14 | "time"
|
14 | 15 |
|
@@ -1007,6 +1008,61 @@ func BenchmarkDecodePathCountryCode(b *testing.B) {
|
1007 | 1008 | require.NoError(b, db.Close(), "error on close")
|
1008 | 1009 | }
|
1009 | 1010 |
|
| 1011 | +// BenchmarkCityLookupConcurrent tests concurrent city lookups to demonstrate |
| 1012 | +// string cache performance under concurrent load. |
| 1013 | +func BenchmarkCityLookupConcurrent(b *testing.B) { |
| 1014 | + db, err := Open("GeoLite2-City.mmdb") |
| 1015 | + require.NoError(b, err) |
| 1016 | + defer func() { |
| 1017 | + require.NoError(b, db.Close(), "error on close") |
| 1018 | + }() |
| 1019 | + |
| 1020 | + // Test with different numbers of concurrent goroutines |
| 1021 | + goroutineCounts := []int{1, 4, 16, 64} |
| 1022 | + |
| 1023 | + for _, numGoroutines := range goroutineCounts { |
| 1024 | + b.Run(fmt.Sprintf("goroutines_%d", numGoroutines), func(b *testing.B) { |
| 1025 | + // Each goroutine performs 100 lookups |
| 1026 | + const lookupsPerGoroutine = 100 |
| 1027 | + b.ResetTimer() |
| 1028 | + |
| 1029 | + for range b.N { |
| 1030 | + var wg sync.WaitGroup |
| 1031 | + wg.Add(numGoroutines) |
| 1032 | + |
| 1033 | + for range numGoroutines { |
| 1034 | + go func() { |
| 1035 | + defer wg.Done() |
| 1036 | + |
| 1037 | + //nolint:gosec // this is a test |
| 1038 | + r := rand.New(rand.NewSource(time.Now().UnixNano())) |
| 1039 | + s := make(net.IP, 4) |
| 1040 | + var result fullCity |
| 1041 | + |
| 1042 | + for range lookupsPerGoroutine { |
| 1043 | + ip := randomIPv4Address(r, s) |
| 1044 | + err := db.Lookup(ip).Decode(&result) |
| 1045 | + if err != nil { |
| 1046 | + b.Error(err) |
| 1047 | + return |
| 1048 | + } |
| 1049 | + // Access string fields to exercise the cache |
| 1050 | + _ = result.City.Names |
| 1051 | + _ = result.Country.Names |
| 1052 | + } |
| 1053 | + }() |
| 1054 | + } |
| 1055 | + |
| 1056 | + wg.Wait() |
| 1057 | + } |
| 1058 | + |
| 1059 | + // Report operations per second |
| 1060 | + totalOps := int64(b.N) * int64(numGoroutines) * int64(lookupsPerGoroutine) |
| 1061 | + b.ReportMetric(float64(totalOps)/b.Elapsed().Seconds(), "lookups/sec") |
| 1062 | + }) |
| 1063 | + } |
| 1064 | +} |
| 1065 | + |
1010 | 1066 | func randomIPv4Address(r *rand.Rand, ip []byte) netip.Addr {
|
1011 | 1067 | num := r.Uint32()
|
1012 | 1068 | ip[0] = byte(num >> 24)
|
|
0 commit comments