]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
common/hreflect: Replace the map/RWMutex method cache with sync.Map
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 26 Mar 2025 10:11:06 +0000 (11:11 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 26 Mar 2025 13:24:51 +0000 (14:24 +0100)
It's much faster when running in parallel:

```
GetMethodByName-10        125.1n ± 6%   181.7n ± 7%  +45.30% (p=0.002 n=6)
GetMethodByNamePara-10   770.10n ± 1%   24.77n ± 9%  -96.78% (p=0.002 n=6)
```

common/hreflect/helpers.go
common/hreflect/helpers_test.go

index ab7883a4785f90667a7dfca8c77d220aec98949b..5453713743f4f4e2edb747f854042692682bb467 100644 (file)
@@ -134,12 +134,7 @@ type methodKey struct {
        name string
 }
 
-type methods struct {
-       sync.RWMutex
-       cache map[methodKey]int
-}
-
-var methodCache = &methods{cache: make(map[methodKey]int)}
+var methodCache sync.Map
 
 // GetMethodByName is the same as reflect.Value.MethodByName, but it caches the
 // type lookup.
@@ -157,22 +152,16 @@ func GetMethodByName(v reflect.Value, name string) reflect.Value {
 // -1 if no such method exists.
 func GetMethodIndexByName(tp reflect.Type, name string) int {
        k := methodKey{tp, name}
-       methodCache.RLock()
-       index, found := methodCache.cache[k]
-       methodCache.RUnlock()
+       v, found := methodCache.Load(k)
        if found {
-               return index
+               return v.(int)
        }
-
-       methodCache.Lock()
-       defer methodCache.Unlock()
-
        m, ok := tp.MethodByName(name)
-       index = m.Index
+       index := m.Index
        if !ok {
                index = -1
        }
-       methodCache.cache[k] = index
+       methodCache.Store(k, index)
 
        if !ok {
                return -1
index 119722261e57a2e4637e56b52909b82f7a8504b1..cbcad0f22e676fd161b30ef75fae3ce5a157f528 100644 (file)
@@ -134,3 +134,17 @@ func BenchmarkGetMethodByName(b *testing.B) {
                }
        }
 }
+
+func BenchmarkGetMethodByNamePara(b *testing.B) {
+       v := reflect.ValueOf(&testStruct{})
+       methods := []string{"Method1", "Method2", "Method3", "Method4", "Method5"}
+
+       b.ResetTimer()
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       for _, method := range methods {
+                               _ = GetMethodByName(v, method)
+                       }
+               }
+       })
+}