From 3c4b709fed0acf75f8ce15b60100ded16886caa8 Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Fri, 15 Apr 2022 17:20:24 -0400 Subject: [PATCH 1/2] Specialize navigating map[string]interface{} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a common type. Specializing it to reduce the use of reflection yields better performance. Similar to https://github.com/pelletier/go-toml/pull/669, there is a lot to explore there. name old time/op new time/op delta UnmarshalDataset/config-8 13.3ms ± 0% 12.3ms ± 0% -7.45% (p=0.008 n=5+5) UnmarshalDataset/canada-8 55.7ms ± 0% 55.2ms ± 0% -0.88% (p=0.008 n=5+5) UnmarshalDataset/citm_catalog-8 16.6ms ± 1% 16.5ms ± 1% -0.97% (p=0.008 n=5+5) UnmarshalDataset/twitter-8 7.10ms ± 1% 7.01ms ± 1% -1.28% (p=0.016 n=5+5) UnmarshalDataset/code-8 63.8ms ± 0% 52.0ms ± 0% -18.45% (p=0.008 n=5+5) UnmarshalDataset/example-8 121µs ± 0% 119µs ± 0% -2.13% (p=0.008 n=5+5) Unmarshal/SimpleDocument/struct-8 438ns ± 1% 432ns ± 1% -1.40% (p=0.008 n=5+5) Unmarshal/SimpleDocument/map-8 594ns ± 2% 573ns ± 1% -3.56% (p=0.008 n=5+5) Unmarshal/ReferenceFile/struct-8 34.3µs ± 1% 33.7µs ± 0% -1.95% (p=0.008 n=5+5) Unmarshal/ReferenceFile/map-8 48.6µs ± 0% 44.2µs ± 1% -9.22% (p=0.008 n=5+5) Unmarshal/HugoFrontMatter-8 7.88µs ± 1% 7.28µs ± 1% -7.66% (p=0.008 n=5+5) name old speed new speed delta UnmarshalDataset/config-8 78.9MB/s ± 0% 85.2MB/s ± 0% +8.05% (p=0.008 n=5+5) UnmarshalDataset/canada-8 39.5MB/s ± 0% 39.9MB/s ± 0% +0.89% (p=0.008 n=5+5) UnmarshalDataset/citm_catalog-8 33.6MB/s ± 1% 33.9MB/s ± 1% +0.98% (p=0.008 n=5+5) UnmarshalDataset/twitter-8 62.3MB/s ± 1% 63.1MB/s ± 1% +1.30% (p=0.016 n=5+5) UnmarshalDataset/code-8 42.1MB/s ± 0% 51.6MB/s ± 0% +22.62% (p=0.008 n=5+5) UnmarshalDataset/example-8 66.9MB/s ± 0% 68.3MB/s ± 0% +2.18% (p=0.008 n=5+5) Unmarshal/SimpleDocument/struct-8 25.1MB/s ± 1% 25.4MB/s ± 1% +1.43% (p=0.008 n=5+5) Unmarshal/SimpleDocument/map-8 18.5MB/s ± 2% 19.2MB/s ± 1% +3.70% (p=0.008 n=5+5) Unmarshal/ReferenceFile/struct-8 153MB/s ± 1% 156MB/s ± 0% +1.99% (p=0.008 n=5+5) Unmarshal/ReferenceFile/map-8 108MB/s ± 0% 119MB/s ± 1% +10.16% (p=0.008 n=5+5) Unmarshal/HugoFrontMatter-8 69.3MB/s ± 1% 75.0MB/s ± 1% +8.30% (p=0.008 n=5+5) name old alloc/op new alloc/op delta UnmarshalDataset/config-8 5.86MB ± 0% 5.26MB ± 0% -10.36% (p=0.008 n=5+5) UnmarshalDataset/canada-8 83.0MB ± 0% 83.0MB ± 0% -0.00% (p=0.008 n=5+5) UnmarshalDataset/citm_catalog-8 34.7MB ± 0% 34.7MB ± 0% -0.04% (p=0.008 n=5+5) UnmarshalDataset/twitter-8 12.7MB ± 0% 12.7MB ± 0% ~ (p=0.548 n=5+5) UnmarshalDataset/code-8 22.2MB ± 0% 15.3MB ± 0% -30.76% (p=0.008 n=5+5) UnmarshalDataset/example-8 186kB ± 0% 186kB ± 0% -0.04% (p=0.008 n=5+5) Unmarshal/SimpleDocument/struct-8 805B ± 0% 805B ± 0% ~ (all equal) Unmarshal/SimpleDocument/map-8 1.13kB ± 0% 1.13kB ± 0% ~ (all equal) Unmarshal/ReferenceFile/struct-8 20.9kB ± 0% 20.9kB ± 0% ~ (all equal) Unmarshal/ReferenceFile/map-8 38.2kB ± 0% 36.4kB ± 0% -4.86% (p=0.029 n=4+4) Unmarshal/HugoFrontMatter-8 7.44kB ± 0% 7.20kB ± 0% -3.23% (p=0.008 n=5+5) name old allocs/op new allocs/op delta UnmarshalDataset/config-8 227k ± 0% 189k ± 0% -16.74% (p=0.029 n=4+4) UnmarshalDataset/canada-8 782k ± 0% 782k ± 0% -0.00% (p=0.008 n=5+5) UnmarshalDataset/citm_catalog-8 192k ± 0% 191k ± 0% -0.49% (p=0.000 n=5+4) UnmarshalDataset/twitter-8 56.9k ± 0% 56.9k ± 0% -0.00% (p=0.032 n=5+5) UnmarshalDataset/code-8 1.05M ± 0% 0.63M ± 0% -40.52% (p=0.008 n=5+5) UnmarshalDataset/example-8 1.36k ± 0% 1.36k ± 0% -0.15% (p=0.008 n=5+5) Unmarshal/SimpleDocument/struct-8 9.00 ± 0% 9.00 ± 0% ~ (all equal) Unmarshal/SimpleDocument/map-8 13.0 ± 0% 13.0 ± 0% ~ (all equal) Unmarshal/ReferenceFile/struct-8 183 ± 0% 183 ± 0% ~ (all equal) Unmarshal/ReferenceFile/map-8 642 ± 0% 526 ± 0% -18.07% (p=0.008 n=5+5) Unmarshal/HugoFrontMatter-8 141 ± 0% 126 ± 0% -10.64% (p=0.008 n=5+5) --- unmarshaler.go | 54 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/unmarshaler.go b/unmarshaler.go index 2219f704..0f81f5cd 100644 --- a/unmarshaler.go +++ b/unmarshaler.go @@ -230,15 +230,6 @@ func (d *decoder) fromParser(root reflect.Value) error { return d.p.Error() } -/* -Rules for the unmarshal code: - -- The stack is used to keep track of which values need to be set where. -- handle* functions <=> switch on a given ast.Kind. -- unmarshalX* functions need to unmarshal a node of kind X. -- An "object" is either a struct or a map. -*/ - func (d *decoder) handleRootExpression(expr *ast.Node, v reflect.Value) error { var x reflect.Value var err error @@ -400,6 +391,46 @@ func (d *decoder) handleArrayTableCollection(key ast.Iterator, v reflect.Value) return d.handleArrayTable(key, v) } +func (d *decoder) handleKeyPartMapStringInterface(key ast.Iterator, m map[string]interface{}, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) { + newMap := false + + k := string(key.Node().Data) + if m == nil { + newMap = true + m = make(map[string]interface{}, 8) + } + + v, ok := m[k] + set := false + + if !ok || v == nil { + set = true + v = makeFn().Interface() + } + + mv := reflect.ValueOf(v) + + x, err := nextFn(key, mv) + if err != nil { + return reflect.Value{}, err + } + + if x.IsValid() { + mv = x + set = true + } + + if set { + m[k] = mv.Interface() + } + + if newMap { + return reflect.ValueOf(m), nil + } + + return reflect.Value{}, nil +} + func (d *decoder) handleKeyPart(key ast.Iterator, v reflect.Value, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) { var rv reflect.Value @@ -416,6 +447,11 @@ func (d *decoder) handleKeyPart(key ast.Iterator, v reflect.Value, nextFn handle case reflect.Map: vt := v.Type() + if vt == mapStringInterfaceType { + m := v.Interface().(map[string]interface{}) + return d.handleKeyPartMapStringInterface(key, m, nextFn, makeFn) + } + // Create the key for the map element. Convert to key type. mk := reflect.ValueOf(string(key.Node().Data)).Convert(vt.Key()) From 8f6d0d8be7fb118b471d052434b22c88789966a3 Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Fri, 15 Apr 2022 17:41:18 -0400 Subject: [PATCH 2/2] Specialize map[string]iface when unmarshaling kvs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit name old time/op new time/op delta UnmarshalDataset/config-8 12.3ms ± 0% 10.9ms ± 0% -11.36% (p=0.008 n=5+5) UnmarshalDataset/canada-8 55.2ms ± 0% 55.4ms ± 1% ~ (p=0.690 n=5+5) UnmarshalDataset/citm_catalog-8 16.5ms ± 1% 15.7ms ± 0% -4.43% (p=0.008 n=5+5) UnmarshalDataset/twitter-8 7.01ms ± 1% 6.63ms ± 0% -5.34% (p=0.008 n=5+5) UnmarshalDataset/code-8 52.0ms ± 0% 48.7ms ± 2% -6.43% (p=0.008 n=5+5) UnmarshalDataset/example-8 119µs ± 0% 110µs ± 3% -6.81% (p=0.008 n=5+5) Unmarshal/SimpleDocument/struct-8 432ns ± 1% 431ns ± 1% ~ (p=0.222 n=5+5) Unmarshal/SimpleDocument/map-8 573ns ± 1% 540ns ± 1% -5.64% (p=0.008 n=5+5) Unmarshal/ReferenceFile/struct-8 33.7µs ± 0% 33.6µs ± 0% ~ (p=0.310 n=5+5) Unmarshal/ReferenceFile/map-8 44.2µs ± 1% 41.7µs ± 1% -5.63% (p=0.008 n=5+5) Unmarshal/HugoFrontMatter-8 7.28µs ± 1% 6.74µs ± 1% -7.42% (p=0.008 n=5+5) [Geo mean] 292µs 277µs -4.91% name old speed new speed delta UnmarshalDataset/config-8 85.2MB/s ± 0% 96.1MB/s ± 0% +12.82% (p=0.008 n=5+5) UnmarshalDataset/canada-8 39.9MB/s ± 0% 39.8MB/s ± 1% ~ (p=0.690 n=5+5) UnmarshalDataset/citm_catalog-8 33.9MB/s ± 1% 35.5MB/s ± 0% +4.64% (p=0.008 n=5+5) UnmarshalDataset/twitter-8 63.1MB/s ± 1% 66.6MB/s ± 0% +5.65% (p=0.008 n=5+5) UnmarshalDataset/code-8 51.6MB/s ± 0% 55.1MB/s ± 2% +6.88% (p=0.008 n=5+5) UnmarshalDataset/example-8 68.3MB/s ± 0% 73.4MB/s ± 3% +7.34% (p=0.008 n=5+5) Unmarshal/SimpleDocument/struct-8 25.4MB/s ± 1% 25.5MB/s ± 1% ~ (p=0.246 n=5+5) Unmarshal/SimpleDocument/map-8 19.2MB/s ± 1% 20.4MB/s ± 1% +5.99% (p=0.008 n=5+5) Unmarshal/ReferenceFile/struct-8 156MB/s ± 0% 156MB/s ± 0% ~ (p=0.310 n=5+5) Unmarshal/ReferenceFile/map-8 119MB/s ± 1% 126MB/s ± 1% +5.97% (p=0.008 n=5+5) Unmarshal/HugoFrontMatter-8 75.0MB/s ± 1% 81.0MB/s ± 1% +8.01% (p=0.008 n=5+5) [Geo mean] 56.1MB/s 59.0MB/s +5.17% name old alloc/op new alloc/op delta UnmarshalDataset/config-8 5.26MB ± 0% 4.75MB ± 0% -9.66% (p=0.008 n=5+5) UnmarshalDataset/canada-8 83.0MB ± 0% 83.0MB ± 0% -0.00% (p=0.008 n=5+5) UnmarshalDataset/citm_catalog-8 34.7MB ± 0% 34.3MB ± 0% -1.13% (p=0.008 n=5+5) UnmarshalDataset/twitter-8 12.7MB ± 0% 12.5MB ± 0% -1.44% (p=0.008 n=5+5) UnmarshalDataset/code-8 15.3MB ± 0% 13.9MB ± 0% -9.27% (p=0.008 n=5+5) UnmarshalDataset/example-8 186kB ± 0% 182kB ± 0% -2.20% (p=0.008 n=5+5) Unmarshal/SimpleDocument/struct-8 805B ± 0% 805B ± 0% ~ (all equal) Unmarshal/SimpleDocument/map-8 1.13kB ± 0% 1.12kB ± 0% -1.41% (p=0.008 n=5+5) Unmarshal/ReferenceFile/struct-8 20.9kB ± 0% 20.9kB ± 0% ~ (all equal) Unmarshal/ReferenceFile/map-8 36.4kB ± 0% 35.4kB ± 0% ~ (p=0.079 n=4+5) Unmarshal/HugoFrontMatter-8 7.20kB ± 0% 6.98kB ± 0% -3.11% (p=0.008 n=5+5) [Geo mean] 312kB 303kB -2.86% name old allocs/op new allocs/op delta UnmarshalDataset/config-8 189k ± 0% 157k ± 0% -16.80% (p=0.029 n=4+4) UnmarshalDataset/canada-8 782k ± 0% 782k ± 0% -0.00% (p=0.008 n=5+5) UnmarshalDataset/citm_catalog-8 191k ± 0% 167k ± 0% -12.75% (p=0.000 n=4+5) UnmarshalDataset/twitter-8 56.9k ± 0% 45.5k ± 0% -20.02% (p=0.016 n=5+4) UnmarshalDataset/code-8 626k ± 0% 537k ± 0% -14.22% (p=0.008 n=5+5) UnmarshalDataset/example-8 1.36k ± 0% 1.11k ± 0% -18.53% (p=0.008 n=5+5) Unmarshal/SimpleDocument/struct-8 9.00 ± 0% 9.00 ± 0% ~ (all equal) Unmarshal/SimpleDocument/map-8 13.0 ± 0% 12.0 ± 0% -7.69% (p=0.008 n=5+5) Unmarshal/ReferenceFile/struct-8 183 ± 0% 183 ± 0% ~ (all equal) Unmarshal/ReferenceFile/map-8 526 ± 0% 466 ± 0% -11.41% (p=0.008 n=5+5) Unmarshal/HugoFrontMatter-8 126 ± 0% 112 ± 0% -11.11% (p=0.008 n=5+5) [Geo mean] 3.73k 3.34k -10.51% --- unmarshaler.go | 53 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/unmarshaler.go b/unmarshaler.go index 0f81f5cd..5e365bbf 100644 --- a/unmarshaler.go +++ b/unmarshaler.go @@ -391,6 +391,44 @@ func (d *decoder) handleArrayTableCollection(key ast.Iterator, v reflect.Value) return d.handleArrayTable(key, v) } +func (d *decoder) handleKeyValuePartMapStringInterface(key ast.Iterator, value *ast.Node, m map[string]interface{}) (reflect.Value, error) { + k := string(key.Node().Data) + + newMap := false + if m == nil { + newMap = true + m = make(map[string]interface{}, 8) + } + + set := false + v, ok := m[k] + if !ok || key.IsLast() { + set = true + v = nil + } + + mv := reflect.ValueOf(&v).Elem() + + x, err := d.handleKeyValueInner(key, value, mv) + if err != nil { + return reflect.Value{}, err + } + if x.IsValid() { + mv = x + set = true + } + + if set { + m[k] = mv.Interface() + } + + if newMap { + return reflect.ValueOf(m), nil + } + + return reflect.Value{}, nil +} + func (d *decoder) handleKeyPartMapStringInterface(key ast.Iterator, m map[string]interface{}, nextFn handlerFn, makeFn valueMakerFn) (reflect.Value, error) { newMap := false @@ -1035,6 +1073,11 @@ func (d *decoder) handleKeyValuePart(key ast.Iterator, value *ast.Node, v reflec case reflect.Map: vt := v.Type() + if vt == mapStringInterfaceType { + m := v.Interface().(map[string]interface{}) + return d.handleKeyValuePartMapStringInterface(key, value, m) + } + mk := reflect.ValueOf(string(key.Node().Data)) mkt := stringType @@ -1058,12 +1101,10 @@ func (d *decoder) handleKeyValuePart(key ast.Iterator, value *ast.Node, v reflec if !mv.IsValid() { set = true mv = reflect.New(v.Type().Elem()).Elem() - } else { - if key.IsLast() { - var x interface{} - mv = reflect.ValueOf(&x).Elem() - set = true - } + } else if key.IsLast() { + var x interface{} + mv = reflect.ValueOf(&x).Elem() + set = true } nv, err := d.handleKeyValueInner(key, value, mv)