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.

Latest run: 16 October 2026 · RouteViews snapshot of 8 August 2026 · AMD Ryzen 7 9800X3D, 16 threads, 59 GiB RAM

2.1×
RustyBGP returns the full table about twice as fast as GoBGP (1.7 s vs 3.6 s, fully binary mode)
5×
quicker first response: 0.45 s vs 2.2–2.6 s before the first routes arrive
~5×
less server CPU per dump (1.5 s vs 8.4 s in binary mode)
−64%
RustyBGP peak memory after its bounded-stream fix (6.9 → 2.5 GiB, structured mode)

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.

MRT snapshot RouteViews, 1.1M Source GoBGP AS 65001 GoBGP 4.9.0 GoBGP 4.10.0 RustyBGP (fixed) C++ gRPC client ListPath, Adj-RIB-In load BGP gRPC

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

ModeWhat the server sends
StructuredEvery route attribute as a typed protobuf message. Easiest to use, most work to build.
Structured + binary attrsStructured routes, plus the raw BGP attribute bytes alongside.
Fully binaryOnly 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

Caveats