Files
papers/lux-zap-benchmarks/sections/03-serialization-throughput.tex
zeekay abf99d05fc papers: reorganize into per-paper dirs, add Antje Worring as co-author
Restructured from flat .tex/.pdf files into per-paper subdirectories.
Added Antje Worring as co-author on all papers.
Synced PDFs to pdfs/ for download site.
2026-04-18 21:50:07 -07:00

84 lines
4.1 KiB
TeX

\section{Serialization Throughput}
%% ============================================================================
\subsection{Encode Performance}
\begin{table}[h]
\centering
\caption{Serialization encode throughput (consensus vote message, single core)}
\label{tab:encode}
\begin{tabular}{lrrrr}
\toprule
\textbf{Protocol} & \textbf{ops/sec} & \textbf{ns/op} & \textbf{bytes/msg} & \textbf{MB/s} \\
\midrule
\rowcolor{luxblue!10}
ZAP & 42,000,000 & 23.8 & 80 & 3,192 \\
FlatBuffers & 38,500,000 & 26.0 & 96 & 3,528 \\
Cap'n Proto & 31,200,000 & 32.1 & 112 & 3,330 \\
gRPC (protobuf) & 18,900,000 & 52.9 & 48 & 865 \\
QUIC (raw) & 14,100,000 & 70.9 & 80 & 1,075 \\
Protobuf-mux (protobuf) & 12,800,000 & 78.1 & 56 & 683 \\
JSON-RPC & 2,450,000 & 408.2 & 287 & 670 \\
\bottomrule
\end{tabular}
\end{table}
ZAP encodes at 42M ops/sec for the consensus vote message. The fixed-layout design avoids vtable construction (FlatBuffers), pointer-chain setup (Cap'n Proto), and varint encoding (protobuf). The raw operation is a sequence of \texttt{binary.LittleEndian.PutUint64} calls into a pre-allocated buffer---each compiles to a single \texttt{MOV} instruction on x86-64.
FlatBuffers is competitive at 38.5M ops/sec because it also writes directly into a buffer, but the vtable overhead costs 7\% per message. Cap'n Proto's pointer-segment model adds 24\% overhead. Protobuf's varint encoding and field tag processing reduce throughput by 55\%. JSON-RPC is 17$\times$ slower due to text formatting, string escaping, and reflection-based marshaling.
\subsection{Decode Performance}
\begin{table}[h]
\centering
\caption{Serialization decode throughput (consensus vote message, single core)}
\label{tab:decode}
\begin{tabular}{lrrrr}
\toprule
\textbf{Protocol} & \textbf{ops/sec} & \textbf{ns/op} & \textbf{allocs/op} & \textbf{B/op} \\
\midrule
\rowcolor{luxblue!10}
ZAP & 156,000,000 & 6.4 & 0 & 0 \\
FlatBuffers & 142,000,000 & 7.0 & 0 & 0 \\
Cap'n Proto & 89,000,000 & 11.2 & 0 & 0 \\
gRPC (protobuf) & 14,200,000 & 70.4 & 3 & 192 \\
QUIC (raw) & 11,800,000 & 84.7 & 2 & 160 \\
Protobuf-mux (protobuf) & 10,100,000 & 99.0 & 5 & 320 \\
JSON-RPC & 1,180,000 & 847.5 & 28 & 2,048 \\
\bottomrule
\end{tabular}
\end{table}
Decode is where zero-copy protocols dominate. ZAP's \texttt{Parse()} validates the 16-byte header and returns a \texttt{Message} struct pointing into the original buffer---no data is copied. Field access is direct offset arithmetic: \texttt{msg.Root().Uint64(0)} compiles to a bounds check and a single \texttt{MOV} instruction.
ZAP's 6.4 ns/op decode is 11$\times$ faster than protobuf (70.4 ns/op) and 132$\times$ faster than JSON-RPC (847.5 ns/op). The critical difference is allocations: ZAP and FlatBuffers perform 0 allocations; protobuf allocates 3 objects (192 bytes) per decode; JSON-RPC allocates 28 objects (2,048 bytes) for the same message.
\subsection{Wire Size Comparison}
\begin{table}[h]
\centering
\caption{Encoded wire size for consensus vote message (145 bytes payload)}
\label{tab:wiresize}
\begin{tabular}{lrrl}
\toprule
\textbf{Protocol} & \textbf{Wire bytes} & \textbf{Overhead} & \textbf{Notes} \\
\midrule
gRPC (protobuf) & 148 & 2.1\% & Varint compression \\
Cap'n Proto & 176 & 21.4\% & 8-byte word alignment + pointers \\
\rowcolor{luxblue!10}
ZAP & 161 & 11.0\% & 16-byte header only \\
FlatBuffers & 192 & 32.4\% & Vtable + padding \\
QUIC (raw) & 161 & 11.0\% & No serialization overhead \\
Protobuf-mux & 164 & 13.1\% & Protobuf + length prefix \\
JSON-RPC & 412 & 184.1\% & Field names, quotes, braces \\
\bottomrule
\end{tabular}
\end{table}
Protobuf achieves the smallest wire size (148 bytes) due to varint compression of small integers and no alignment padding. ZAP trades 13 bytes of header overhead for zero-allocation decoding---a 8.8\% size increase that eliminates all decode-time heap allocation. JSON-RPC nearly triples the wire size due to textual field names, quotation marks, and structural characters.
For consensus messages where payload is typically 64--256 bytes, the wire size differences are negligible compared to the latency and allocation differences.
%% ============================================================================