Skip to content

Commit 380c17e

Browse files
committed
Use MapIter.SetKey/SetValue and sync.Pool to improve memory allocation
Since go 1.18, the reflect package introduces MapIter.SetKey and MapIter.SetValue that will do fewer memory allocation for map iteration which is frequently used for CBOR encode operation. Plus, usage of sync.Pool will further reduce memory allocation by reusing the shared memory in the pool. Lastly, the Value.SetZero method (available since go 1.20) is helpful to release memory allocation to the GC when is no longer needed. Signed-off-by: Vu Dinh <vudinh@outlook.com>
1 parent e5eaf7a commit 380c17e

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

encode_map.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) Faye Amacker. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
//go:build go1.20
5+
6+
package cbor
7+
8+
import (
9+
"reflect"
10+
"sync"
11+
)
12+
13+
type mapKeyValueEncodeFunc struct {
14+
kf, ef encodeFunc
15+
kpool, vpool sync.Pool
16+
}
17+
18+
func (me *mapKeyValueEncodeFunc) encodeKeyValues(e *encoderBuffer, em *encMode, v reflect.Value, kvs []keyValue) error {
19+
trackKeyValueLength := len(kvs) == v.Len()
20+
iterk := me.kpool.Get().(*reflect.Value)
21+
defer func() {
22+
iterk.SetZero()
23+
me.kpool.Put(iterk)
24+
}()
25+
iterv := me.vpool.Get().(*reflect.Value)
26+
defer func() {
27+
iterv.SetZero()
28+
me.vpool.Put(iterv)
29+
}()
30+
iter := v.MapRange()
31+
for i := 0; iter.Next(); i++ {
32+
off := e.Len()
33+
iterk.SetIterKey(iter)
34+
iterv.SetIterValue(iter)
35+
36+
if err := me.kf(e, em, *iterk); err != nil {
37+
return err
38+
}
39+
if trackKeyValueLength {
40+
kvs[i].keyLen = e.Len() - off
41+
}
42+
43+
if err := me.ef(e, em, *iterv); err != nil {
44+
return err
45+
}
46+
if trackKeyValueLength {
47+
kvs[i].keyValueLen = e.Len() - off
48+
}
49+
}
50+
51+
return nil
52+
}
53+
54+
func getEncodeMapFunc(t reflect.Type) encodeFunc {
55+
kf, _ := getEncodeFunc(t.Key())
56+
ef, _ := getEncodeFunc(t.Elem())
57+
if kf == nil || ef == nil {
58+
return nil
59+
}
60+
mkv := &mapKeyValueEncodeFunc{
61+
kf: kf,
62+
ef: ef,
63+
kpool: sync.Pool{
64+
New: func() interface{} {
65+
rk := reflect.New(t.Key()).Elem()
66+
return &rk
67+
},
68+
},
69+
vpool: sync.Pool{
70+
New: func() interface{} {
71+
rv := reflect.New(t.Elem()).Elem()
72+
return &rv
73+
},
74+
},
75+
}
76+
return mapEncodeFunc{
77+
e: mkv.encodeKeyValues,
78+
}.encode
79+
}

encode_map_go117.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Faye Amacker. All rights reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4+
//go:build !go1.20
5+
46
package cbor
57

68
import (

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module github.com/fxamacker/cbor/v2
22

3-
go 1.12
3+
go 1.20
44

55
require github.com/x448/float16 v0.8.4

0 commit comments

Comments
 (0)