]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
common/hreflect: Speed up IsTrutfulValue
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 29 Sep 2025 09:34:25 +0000 (11:34 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Mon, 29 Sep 2025 15:06:50 +0000 (17:06 +0200)
By caching the calculation of whether a type implements `IsZero`:

```
IsTruthFulVAlue-10    467.95n ± ∞ ¹   79.13n ± ∞ ¹  -83.09% (p=0.029 n=4)
```

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

index abb9df6b15213f1d281658f3ff1d39ef21ba0584..81fc0cde47e5770d50b296e762e7f7fcf45a5c54 100644 (file)
@@ -86,6 +86,18 @@ func IsSlice(v any) bool {
 
 var zeroType = reflect.TypeOf((*types.Zeroer)(nil)).Elem()
 
+var isZeroCache sync.Map
+
+func implementsIsZero(tp reflect.Type) bool {
+       v, ok := isZeroCache.Load(tp)
+       if ok {
+               return v.(bool)
+       }
+       implements := tp.Implements(zeroType)
+       isZeroCache.Store(tp, implements)
+       return implements
+}
+
 // IsTruthfulValue returns whether the given value has a meaningful truth value.
 // This is based on template.IsTrue in Go's stdlib, but also considers
 // IsZero and any interface value will be unwrapped before it's considered
@@ -105,7 +117,7 @@ func IsTruthfulValue(val reflect.Value) (truth bool) {
                return
        }
 
-       if val.Type().Implements(zeroType) {
+       if implementsIsZero(val.Type()) {
                return !val.Interface().(types.Zeroer).IsZero()
        }
 
index 211172d3998868da6e93ce1296bb6141916f0bf8..61361c56c28e425b4356d419abeb853e61a33da3 100644 (file)
@@ -106,14 +106,26 @@ func BenchmarkIsContextType(b *testing.B) {
        })
 }
 
-func BenchmarkIsTruthFul(b *testing.B) {
-       v := reflect.ValueOf("Hugo")
+func BenchmarkIsTruthFulValue(b *testing.B) {
+       var (
+               stringHugo  = reflect.ValueOf("Hugo")
+               stringEmpty = reflect.ValueOf("")
+               zero        = reflect.ValueOf(time.Time{})
+               timeNow     = reflect.ValueOf(time.Now())
+               boolTrue    = reflect.ValueOf(true)
+               boolFalse   = reflect.ValueOf(false)
+               nilPointer  = reflect.ValueOf((*zeroStruct)(nil))
+       )
 
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
-               if !IsTruthfulValue(v) {
-                       b.Fatal("not truthful")
-               }
+               IsTruthfulValue(stringHugo)
+               IsTruthfulValue(stringEmpty)
+               IsTruthfulValue(zero)
+               IsTruthfulValue(timeNow)
+               IsTruthfulValue(boolTrue)
+               IsTruthfulValue(boolFalse)
+               IsTruthfulValue(nilPointer)
        }
 }