http2: use an array instead of a map in typeFrameParser
FrameType is a dense integer range, so we can store the frameParsers
in an array instead of a map. This should be a very small performance
win on all Go http2 servers. For high QPS gRPC services, this function
is visible in the Go profiler. For example, it shows up as 0.16% of
all CPU time on one production service at Datadog.
Change FrameType.String() to use the same pattern.
Add a test for testFrameType with unknown FrameTypes.
Fixes golang/go#73613
Change-Id: I5f5b523e011a99d6b428cbdbfd97415e488169d1
Reviewed-on: https://go-review.googlesource.com/c/net/+/670415
Reviewed-by: Sean Liao <sean@liao.dev>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/http2/frame.go b/http2/frame.go
index 97bd8b0..e6f728c 100644
--- a/http2/frame.go
+++ b/http2/frame.go
@@ -39,7 +39,7 @@
FrameContinuation FrameType = 0x9
)
-var frameName = map[FrameType]string{
+var frameNames = [...]string{
FrameData: "DATA",
FrameHeaders: "HEADERS",
FramePriority: "PRIORITY",
@@ -53,10 +53,10 @@
}
func (t FrameType) String() string {
- if s, ok := frameName[t]; ok {
- return s
+ if int(t) < len(frameNames) {
+ return frameNames[t]
}
- return fmt.Sprintf("UNKNOWN_FRAME_TYPE_%d", uint8(t))
+ return fmt.Sprintf("UNKNOWN_FRAME_TYPE_%d", t)
}
// Flags is a bitmask of HTTP/2 flags.
@@ -124,7 +124,7 @@
// might be 0).
type frameParser func(fc *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error)
-var frameParsers = map[FrameType]frameParser{
+var frameParsers = [...]frameParser{
FrameData: parseDataFrame,
FrameHeaders: parseHeadersFrame,
FramePriority: parsePriorityFrame,
@@ -138,8 +138,8 @@
}
func typeFrameParser(t FrameType) frameParser {
- if f := frameParsers[t]; f != nil {
- return f
+ if int(t) < len(frameParsers) {
+ return frameParsers[t]
}
return parseUnknownFrame
}
diff --git a/http2/frame_test.go b/http2/frame_test.go
index 86e5d4f..6850531 100644
--- a/http2/frame_test.go
+++ b/http2/frame_test.go
@@ -1258,3 +1258,21 @@
}
}
+
+func TestTypeFrameParser(t *testing.T) {
+ if len(frameNames) != len(frameParsers) {
+ t.Errorf("expected len(frameNames)=%d to equal len(frameParsers)=%d",
+ len(frameNames), len(frameParsers))
+ }
+
+ // typeFrameParser() for an unknown type returns a function that returns UnknownFrame
+ unknownFrameType := FrameType(FrameContinuation + 1)
+ unknownParser := typeFrameParser(unknownFrameType)
+ frame, err := unknownParser(nil, FrameHeader{}, nil, nil)
+ if err != nil {
+ t.Errorf("unknownParser() must not return an error: %v", err)
+ }
+ if _, isUnknown := frame.(*UnknownFrame); !isUnknown {
+ t.Errorf("expected UnknownFrame, got %T", frame)
+ }
+}