From: Bjørn Erik Pedersen Date: Mon, 29 Sep 2025 09:34:25 +0000 (+0200) Subject: common/hreflect: Speed up IsTrutfulValue X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=4d1303512b6e1d7c2fd89777f30abded914e2279;p=brevno-suite%2Fhugo common/hreflect: Speed up IsTrutfulValue By caching the calculation of whether a type implements `IsZero`: ``` IsTruthFulVAlue-10 467.95n ± ∞ ¹ 79.13n ± ∞ ¹ -83.09% (p=0.029 n=4) ``` --- diff --git a/common/hreflect/helpers.go b/common/hreflect/helpers.go index abb9df6b1..81fc0cde4 100644 --- a/common/hreflect/helpers.go +++ b/common/hreflect/helpers.go @@ -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() } diff --git a/common/hreflect/helpers_test.go b/common/hreflect/helpers_test.go index 211172d39..61361c56c 100644 --- a/common/hreflect/helpers_test.go +++ b/common/hreflect/helpers_test.go @@ -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) } }