tpl/collections: Use MapRange/SetIterKey/SetIterValue for Where, Sort and Merge - hugo - [fork] hugo port for 9front
HTML git clone git@git.drkhsh.at/hugo.git
DIR Log
DIR Files
DIR Refs
DIR Submodules
DIR README
DIR LICENSE
---
DIR commit de7137cc3527bbe77fae55f30d60f69ff6cc4157
DIR parent a2edf04c2749153ac9daf1cce2b355d3e61ad0e9
HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Date: Sun, 12 Jan 2025 17:58:11 +0200
tpl/collections: Use MapRange/SetIterKey/SetIterValue for Where, Sort and Merge
Some relevant benchmarks:
Where with maps:
```
cpu: Apple M1 Pro
│ master.bench │ fix-mapkeys.bench │
│ sec/op │ sec/op vs base │
WhereMap-10 79.26µ ± 1% 26.58µ ± 1% -66.46% (p=0.002 n=6)
│ master.bench │ fix-mapkeys.bench │
│ B/op │ B/op vs base │
WhereMap-10 56685.0 ± 0% 111.0 ± 1% -99.80% (p=0.002 n=6)
│ master.bench │ fix-mapkeys.bench │
│ allocs/op │ allocs/op vs base │
WhereMap-10 2003.000 ± 0% 4.000 ± 0% -99.80% (p=0.002 n=6)
```
Merge:
```
│ master.bench │ fix-mapkeys.bench │
│ sec/op │ sec/op vs base │
Merge-10 3.285µ ± 0% 2.268µ ± 1% -30.96% (p=0.002 n=6)
│ master.bench │ fix-mapkeys.bench │
│ B/op │ B/op vs base │
Merge-10 3.079Ki ± 0% 1.891Ki ± 0% -38.58% (p=0.002 n=6)
│ master.bench │ fix-mapkeys.bench │
│ allocs/op │ allocs/op vs base │
Merge-10 64.00 ± 0% 26.00 ± 0% -59.38% (p=0.002 n=6)
```
Sort:
```
cpu: Apple M1 Pro
│ master.bench │ fix-mapkeys.bench │
│ sec/op │ sec/op vs base │
SortMap-10 1008.0n ± 1% 915.5n ± 0% -9.18% (p=0.002 n=6)
│ master.bench │ fix-mapkeys.bench │
│ B/op │ B/op vs base │
SortMap-10 640.0 ± 0% 512.0 ± 0% -20.00% (p=0.002 n=6)
│ master.bench │ fix-mapkeys.bench │
│ allocs/op │ allocs/op vs base │
SortMap-10 16.00 ± 0% 15.00 ± 0% -6.25% (p=0.002 n=6)
```
Diffstat:
M tpl/collections/merge.go | 42 +++++++++++++++++++++----------
M tpl/collections/sort.go | 16 ++++++++++------
M tpl/collections/where.go | 10 ++++++----
3 files changed, 45 insertions(+), 23 deletions(-)
---
DIR diff --git a/tpl/collections/merge.go b/tpl/collections/merge.go
@@ -75,9 +75,13 @@ func caseInsensitiveLookup(m, k reflect.Value) (reflect.Value, bool) {
return v, hreflect.IsTruthfulValue(v)
}
- for _, key := range m.MapKeys() {
- if strings.EqualFold(k.String(), key.String()) {
- return m.MapIndex(key), true
+ k2 := reflect.New(m.Type().Key()).Elem()
+
+ iter := m.MapRange()
+ for iter.Next() {
+ k2.SetIterKey(iter)
+ if strings.EqualFold(k.String(), k2.String()) {
+ return iter.Value(), true
}
}
@@ -90,17 +94,28 @@ func mergeMap(dst, src reflect.Value) reflect.Value {
// If the destination is Params, we must lower case all keys.
_, lowerCase := dst.Interface().(maps.Params)
+ k := reflect.New(dst.Type().Key()).Elem()
+ v := reflect.New(dst.Type().Elem()).Elem()
+
// Copy the destination map.
- for _, key := range dst.MapKeys() {
- v := dst.MapIndex(key)
- out.SetMapIndex(key, v)
+ iter := dst.MapRange()
+ for iter.Next() {
+ k.SetIterKey(iter)
+ v.SetIterValue(iter)
+ out.SetMapIndex(k, v)
}
// Add all keys in src not already in destination.
// Maps of the same type will be merged.
- for _, key := range src.MapKeys() {
- sv := src.MapIndex(key)
- dv, found := caseInsensitiveLookup(dst, key)
+ k = reflect.New(src.Type().Key()).Elem()
+ sv := reflect.New(src.Type().Elem()).Elem()
+
+ iter = src.MapRange()
+ for iter.Next() {
+ sv.SetIterValue(iter)
+ k.SetIterKey(iter)
+
+ dv, found := caseInsensitiveLookup(dst, k)
if found {
// If both are the same map key type, merge.
@@ -112,14 +127,15 @@ func mergeMap(dst, src reflect.Value) reflect.Value {
}
if dve.Type().Key() == sve.Type().Key() {
- out.SetMapIndex(key, mergeMap(dve, sve))
+ out.SetMapIndex(k, mergeMap(dve, sve))
}
}
} else {
- if lowerCase && key.Kind() == reflect.String {
- key = reflect.ValueOf(strings.ToLower(key.String()))
+ kk := k
+ if lowerCase && k.Kind() == reflect.String {
+ kk = reflect.ValueOf(strings.ToLower(k.String()))
}
- out.SetMapIndex(key, sv)
+ out.SetMapIndex(kk, sv)
}
}
DIR diff --git a/tpl/collections/sort.go b/tpl/collections/sort.go
@@ -99,18 +99,21 @@ func (ns *Namespace) Sort(ctx context.Context, l any, args ...any) (any, error)
}
case reflect.Map:
- keys := seqv.MapKeys()
- for i := 0; i < seqv.Len(); i++ {
- p.Pairs[i].Value = seqv.MapIndex(keys[i])
+ iter := seqv.MapRange()
+ i := 0
+ for iter.Next() {
+ key := iter.Key()
+ value := iter.Value()
+ p.Pairs[i].Value = value
if sortByField == "" {
- p.Pairs[i].Key = keys[i]
+ p.Pairs[i].Key = key
} else if sortByField == "value" {
p.Pairs[i].Key = p.Pairs[i].Value
} else {
v := p.Pairs[i].Value
var err error
- for i, elemName := range path {
+ for j, elemName := range path {
v, err = evaluateSubElem(ctxv, v, elemName)
if err != nil {
return nil, err
@@ -120,12 +123,13 @@ func (ns *Namespace) Sort(ctx context.Context, l any, args ...any) (any, error)
}
// Special handling of lower cased maps.
if params, ok := v.Interface().(maps.Params); ok {
- v = reflect.ValueOf(params.GetNested(path[i+1:]...))
+ v = reflect.ValueOf(params.GetNested(path[j+1:]...))
break
}
}
p.Pairs[i].Key = v
}
+ i++
}
}
DIR diff --git a/tpl/collections/where.go b/tpl/collections/where.go
@@ -409,7 +409,6 @@ func (ns *Namespace) checkWhereArray(ctxv, seqv, kv, mv reflect.Value, path []st
for i, elemName := range path {
var err error
vvv, err = evaluateSubElem(ctxv, vvv, elemName)
-
if err != nil {
continue
}
@@ -442,9 +441,12 @@ func (ns *Namespace) checkWhereArray(ctxv, seqv, kv, mv reflect.Value, path []st
// checkWhereMap handles the where-matching logic when the seqv value is a Map.
func (ns *Namespace) checkWhereMap(ctxv, seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
rv := reflect.MakeMap(seqv.Type())
- keys := seqv.MapKeys()
- for _, k := range keys {
- elemv := seqv.MapIndex(k)
+ k := reflect.New(seqv.Type().Key()).Elem()
+ elemv := reflect.New(seqv.Type().Elem()).Elem()
+ iter := seqv.MapRange()
+ for iter.Next() {
+ k.SetIterKey(iter)
+ elemv.SetIterValue(iter)
switch elemv.Kind() {
case reflect.Array, reflect.Slice:
r, err := ns.checkWhereArray(ctxv, elemv, kv, mv, path, op)