gopls/internal/lsp/cache: frob: a fast encoder with gob-like powers
This change creates new package, frob, that provides an
encoder and decoder of certain Go data structures in
the spirit of the "encoding/gob" package. However,
it requires the user to construct a codec ahead of time,
which saves time during encoding/decoding, and it supports
only a subset of what gob supports, notably excluding
interfaces, which greatly simplifies the implementation
as it doesn't need to encode runtime types
It was found to be at least 15x faster in certain pathological
cases of gopls where gob encoding was a major cost.
(Specifically, the mustDecode operation of analysis.)
Also, because it avoids runtime types, the encoding is
more compact. It reduces gopls' overall file-based cache
usage (and corresponding RAM-based LRU cache) by 34%.
Change-Id: Ia11193ed89465d6a8e7bfd0d2aa29694f7ab7d14
Reviewed-on: https://go-review.googlesource.com/c/tools/+/510497
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/lsp/source/methodsets/methodsets.go
index 56b8ce3..1ade740 100644
--- a/gopls/internal/lsp/source/methodsets/methodsets.go
+++ b/gopls/internal/lsp/source/methodsets/methodsets.go
@@ -44,17 +44,15 @@
// single 64-bit mask is quite effective. See CL 452060 for details.
import (
- "bytes"
- "encoding/gob"
"fmt"
"go/token"
"go/types"
"hash/crc32"
- "log"
"strconv"
"strings"
"golang.org/x/tools/go/types/objectpath"
+ "golang.org/x/tools/gopls/internal/lsp/frob"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
"golang.org/x/tools/internal/typeparams"
)
@@ -69,27 +67,13 @@
// Decode decodes the given gob-encoded data as an Index.
func Decode(data []byte) *Index {
var pkg gobPackage
- mustDecode(data, &pkg)
+ packageCodec.Decode(data, &pkg)
return &Index{pkg}
}
// Encode encodes the receiver as gob-encoded data.
func (index *Index) Encode() []byte {
- return mustEncode(index.pkg)
-}
-
-func mustEncode(x interface{}) []byte {
- var buf bytes.Buffer
- if err := gob.NewEncoder(&buf).Encode(x); err != nil {
- log.Fatalf("internal error encoding %T: %v", x, err)
- }
- return buf.Bytes()
-}
-
-func mustDecode(data []byte, ptr interface{}) {
- if err := gob.NewDecoder(bytes.NewReader(data)).Decode(ptr); err != nil {
- log.Fatalf("internal error decoding %T: %v", ptr, err)
- }
+ return packageCodec.Encode(index.pkg)
}
// NewIndex returns a new index of method-set information for all
@@ -470,9 +454,9 @@
// -- serial format of index --
-// The cost of gob encoding and decoding for most packages in x/tools
-// is under 50us, with occasional peaks of around 1-3ms.
-// The encoded indexes are around 1KB-50KB.
+// (The name says gob but in fact we use frob.)
+// var packageCodec = frob.For[gobPackage]()
+var packageCodec = frob.CodecFor117(new(gobPackage))
// A gobPackage records the method set of each package-level type for a single package.
type gobPackage struct {