How fast can a BGP daemon hand over a full Internet table?
We asked GoBGP and RustyBGP for the complete IPv4 table (1.1 million routes) through the gRPC ListPath API, the way a monitoring tool like FastNetMon does, and measured time, CPU and memory.
This measures API retrieval only. It says nothing about BGP convergence, route propagation, update throughput or policy evaluation, so don't read it as "which daemon routes better".
What we measured
One GoBGP "source" daemon loads the RouteViews snapshot and announces it to each daemon under test. Once every target holds the same 1,115,966 routes, a small C++ client asks for the whole table and times the stream.
Everything runs in Docker on one host. Requests are sequential and shuffled; each value is the median of 5 requests after 1 warm-up. Every daemon's answer is checked by SHA-256 over all routes, so all three return identical data.
Three ways to ask for the table
| Mode | What the server sends |
|---|---|
| Structured | Every route attribute as a typed protobuf message. Easiest to use, most work to build. |
| Structured + binary attrs | Structured routes, plus the raw BGP attribute bytes alongside. |
| Fully binary | Only the raw wire-format NLRI and attribute bytes. Cheapest for the server; the client decodes. |
1 · Time to fetch the full table
Wall-clock time from request to the last route received. Shorter is better.
2 · Time until the first routes arrive
GoBGP builds a large part of its answer before sending anything. RustyBGP starts streaming in under half a second.
3 · Server CPU spent per dump
User + system CPU the daemon burned while serving one full-table request. GoBGP's CPU time is higher than its wall-clock time because the work (including Go's garbage collector) runs on several cores at once.
4 · Memory: loaded table vs peak during one dump
Each bar is a fresh daemon serving exactly one request. The pale part is memory used just holding the table; the full bar is the peak reached while serving the dump.
GoBGP 4.10 holds the table in ~0.3 GiB less memory than 4.9, but both climb to 2.2–3.3 GiB during a dump and stay there afterwards (Go doesn't hand the memory back promptly). RustyBGP peaks at ~2.5 GiB and drops back to ~1.4–1.6 GiB once idle. These are single observations, not medians.
5 · The RustyBGP fix that made this possible
Unpatched RustyBGP converted the entire table to protobuf messages before sending the first one. The fix/listpath-bounded-stream change converts routes only as fast as the client reads them (a 1,024-message buffer), so memory stays flat and the stream starts at once.
Peak memory, one dump
Time to fetch full table
Before: commit 603d350; after: abab831. Same compiler, Cargo.lock and build profile. Over a longer workload that ran all three modes back to back, lifetime peak fell from 12.2 GiB to 2.9 GiB.
What this means
- Ask for binary if you can. In every daemon, fully binary mode is the fastest and cheapest: about 15% less time and 20–30% less server CPU than structured mode.
- "Structured + binary attributes" is the worst of both. The server builds both representations, so it's the slowest mode everywhere.
- GoBGP 4.9 → 4.10 is roughly a wash for dumps. 4.10 is 1–6% slower in our medians, inside the run-to-run spread, and loads the table with less memory.
- RustyBGP with the bounded-stream fix is about 2× faster end to end, about 5× faster to first byte, and uses much less CPU, at a similar peak memory to GoBGP.
Caveats
- One peer, one client, sequential requests on one machine. Slow consumers, concurrent dumps and multi-peer tables are separate experiments.
- The client scans the binary bytes but does not fully decode BGP attributes, so real-world client cost will be higher in binary mode.
- Server CPU is the process's total CPU around each request, not a profile of the handler.