]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/binary: cache struct sizes to speed up Read and Write for slice of structs.
authorkwakubiney <kwakubiney@gmail.com>
Mon, 11 Mar 2024 22:53:08 +0000 (22:53 +0000)
committerGopher Robot <gobot@golang.org>
Wed, 13 Mar 2024 18:32:53 +0000 (18:32 +0000)
A lot of allocations happen in dataSize due to reflection.

Cache the result of the function when encoding a
slice of structs similar to what is done for struct types
so that subsequent calls to dataSize can avoid allocations.

                        │   old.txt   │            new.txt            │
                        │   sec/op    │   sec/op     vs base          │
WriteSlice1000Structs-2   846.7µ ± 4%   856.4µ ± 3%  ~ (p=0.602 n=20)

                        │   old.txt    │            new.txt             │
                        │     B/s      │     B/s       vs base          │
WriteSlice1000Structs-2   84.48Mi ± 4%   83.52Mi ± 3%  ~ (p=0.602 n=20)

                        │   old.txt    │               new.txt               │
                        │     B/op     │     B/op      vs base               │
WriteSlice1000Structs-2   80.18Ki ± 0%   80.06Ki ± 0%  -0.15% (p=0.000 n=20)

                        │   old.txt   │              new.txt               │
                        │  allocs/op  │ allocs/op   vs base                │
WriteSlice1000Structs-2   16.000 ± 0%   1.000 ± 0%  -93.75% (p=0.000 n=2

                       │   old.txt   │              new.txt               │
                       │   sec/op    │   sec/op     vs base               │
ReadSlice1000Structs-2   847.4µ ± 4%   821.1µ ± 3%  -3.10% (p=0.012 n=20)

                       │   old.txt    │               new.txt               │
                       │     B/s      │     B/s       vs base               │
ReadSlice1000Structs-2   84.40Mi ± 4%   87.11Mi ± 3%  +3.20% (p=0.012 n=20)

                       │   old.txt    │               new.txt               │
                       │     B/op     │     B/op      vs base               │
ReadSlice1000Structs-2   80.12Ki ± 0%   80.00Ki ± 0%  -0.15% (p=0.000 n=20)

                       │   old.txt   │              new.txt               │
                       │  allocs/op  │ allocs/op   vs base                │
ReadSlice1000Structs-2   16.000 ± 0%   1.000 ± 0%  -93.75% (p=0.000 n=20)

Fixes #66253

Change-Id: I8227e61306db1fe103489ea4fee2429247c3debc
Reviewed-on: https://go-review.googlesource.com/c/go/+/570855
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>

src/encoding/binary/binary.go
src/encoding/binary/binary_test.go

index 59a6c654d2ad7f63f3d2a4b9c92589e6f1920748..634995a5bd8b7ebde5bc2216dd9f0982ce6a2df4 100644 (file)
@@ -480,8 +480,17 @@ var structSize sync.Map // map[reflect.Type]int
 func dataSize(v reflect.Value) int {
        switch v.Kind() {
        case reflect.Slice:
-               if s := sizeof(v.Type().Elem()); s >= 0 {
-                       return s * v.Len()
+               t := v.Type().Elem()
+               if size, ok := structSize.Load(t); ok {
+                       return size.(int) * v.Len()
+               }
+
+               size := sizeof(t)
+               if size >= 0 {
+                       if t.Kind() == reflect.Struct {
+                               structSize.Store(t, size)
+                       }
+                       return size * v.Len()
                }
 
        case reflect.Struct:
index 4b22b28843660113c95a1ef8e6c43ce7e5edbf60..6cd0b92fa30260b8e4962dfa82683280d54661f8 100644 (file)
@@ -631,6 +631,31 @@ func BenchmarkWriteStruct(b *testing.B) {
        }
 }
 
+func BenchmarkWriteSlice1000Structs(b *testing.B) {
+       slice := make([]Struct, 1000)
+       buf := new(bytes.Buffer)
+       var w io.Writer = buf
+       b.SetBytes(int64(Size(slice)))
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               buf.Reset()
+               Write(w, BigEndian, slice)
+       }
+       b.StopTimer()
+}
+
+func BenchmarkReadSlice1000Structs(b *testing.B) {
+       bsr := &byteSliceReader{}
+       slice := make([]Struct, 1000)
+       buf := make([]byte, Size(slice))
+       b.SetBytes(int64(len(buf)))
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               bsr.remain = buf
+               Read(bsr, BigEndian, slice)
+       }
+}
+
 func BenchmarkReadInts(b *testing.B) {
        var ls Struct
        bsr := &byteSliceReader{}