Skip to content

perf: engine-independent decode wins (DiscardUnknown, HTTP buffer pool, PGO tooling) - #389

Merged
jfberry merged 1 commit into
mainfrom
perf/decode-cheap-wins
Jul 29, 2026
Merged

perf: engine-independent decode wins (DiscardUnknown, HTTP buffer pool, PGO tooling)#389
jfberry merged 1 commit into
mainfrom
perf/decode-cheap-wins

Conversation

@jfberry

@jfberry jfberry commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

What & why

Three engine-independent decode optimizations — they need no change to the proto decoder or schema, are cheap, and are safe to ship on their own. Extracted from the decode-performance investigation (see docs/decode-performance-findings.md) after the hyperpb engine swap was abandoned as too immature.

1. DiscardUnknown on inbound client protos

Every inbound game-client proto is now decoded through unmarshalClientProto (proto.UnmarshalOptions{DiscardUnknown: true}) instead of proto.Unmarshal. Golbat is read-only against these protos and never re-marshals them, so retaining unknown fields is pure overhead. Measured +~3.7% decode rate on its own.

Golbat's own internal proto (GolbatInternal in decoder/pokemon_decode.go) intentionally keeps plain proto.Unmarshal — it round-trips and must retain unknowns.

2. HTTP base64 buffer pool

The HTTP /raw ingest path base64-decodes payloads into fresh buffers per request. raw_bufpool.go pools those buffers (sync.Pool), verified 0 allocs/op on the decode step. Note: this only helps the HTTP ingest path — the gRPC path already receives raw []byte and grpc-go pools its transport buffers, so there's nothing of ours to pool there.

3. PGO tooling (Makefile)

make pgo-capture / pgo-status / pgo-config targets to capture a live CPU profile and drop it at default.pgo, which the Go compiler (≥1.21) applies automatically. No code change — just tooling to make profile-guided optimization repeatable. (A default.pgo already lives in the repo; these targets refresh it.)

Safety / testing

  • go build ./... and go test ./... green (all 7 packages).
  • raw_bufpool_test.go covers pool round-trip/reuse/concurrency and that DiscardUnknown drops unknown fields while keeping known ones.

Relationship to #388 (proto thinning)

The DiscardUnknown change here is also in #388, where it's a prerequisite for schema thinning (without it, thinned-away fields are retained as raw unknown-field blobs, negating the win). The two copies are the identical change, so they don't conflict — whichever merges second, the overlap is a no-op (or a trivial rebase). This PR additionally carries the HTTP buffer pool and PGO tooling, which #388 does not. They can merge in either order.

…l, PGO tooling)

Salvages the measured, engine-independent optimizations from the hyperpb
exploration (now being left behind for its v0.1.x immaturity):

- DiscardUnknown on all inbound client-proto unmarshals via a shared
  unmarshalClientProto helper (decode.go/decode_nebula.go/decode_push_gateway.go).
  +3.7% decode rate, -5.5% objects. The universal win: applies on BOTH the
  gRPC and HTTP ingest paths, which share decode()'s unmarshal.
- Ingest buffer pooling (raw_bufpool.go) for the HTTP /raw base64 payload:
  0 allocs/op, -14% bytes on that path. HTTP-only by nature — the gRPC
  majority has no base64 buffer of ours to pool, and grpc-go v1.81 already
  pools its transport buffers by default; those users get their win from
  DiscardUnknown. Safe because std protobuf-go copies bytes out on Unmarshal.
- make pgo-capture/pgo-status, extended to auto-detect port + api_secret
  from config.toml (env still overrides; make pgo-config shows the target).
  The base committed default.pgo but not the tooling to refresh it.

Tests: pool round-trip/reuse/concurrent (race-clean) + DiscardUnknown
drops-unknown-keeps-known. Full suite + vet green.
docs/decode-performance-findings.md collects the full retrospective
(what's applied, what's already on base, reusable rig, dead-ends, method).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jfberry
jfberry merged commit 200c6d9 into main Jul 29, 2026
6 checks passed
@jfberry
jfberry deleted the perf/decode-cheap-wins branch July 29, 2026 10:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant