Commit 4827725
perf(rpc): rewrite SerializeToIpcBytes around the payload API
SerializeToIpcBytes was the canonical "Arrow batch -> std::vector<uint8_t>"
helper used by every aggregate, table_in_out, table_buffering, bind and
init code path. The body wrote into a BufferOutputStream via MakeStreamWriter
(which geometrically grows the underlying heap buffer through realloc +
memcpy as bytes accumulate), then Finish()'d to a Buffer, then copied that
Buffer into a freshly allocated std::vector. Two effectively redundant
allocations and one full copy per call.
Drive arrow::ipc's payload API by hand instead:
GetSchemaPayload -> schema FlatBuffer once
CollectDictionaries / -> dictionary batch payloads (empty for non-dict
GetDictionaryPayload schemas, no extra cost)
GetRecordBatchPayload -> batch FlatBuffer + body buffers once
GetPayloadSize -> exact size for each
std::vector<uint8_t> allocated once at the precomputed total
WriteIpcPayload writes each piece into a FixedSizeBufferWriter
slice of the destination
Single Assemble pass, no realloc chain, no final copy.
Dictionary handling is the subtle part. The Arrow IPC streaming format
requires a dictionary-batch message between the schema and record-batch
messages for every column that uses a dictionary encoding (DuckDB enums,
some struct fields). MakeStreamWriter's IpcFormatWriter handles this via
WriteDictionaries(batch); replicating it here is mandatory or every
enum-bearing table fails to round-trip. The first attempt at this commit
skipped the dictionary messages and broke ~20 integration tests
(filter_pushdown/enums, settings/*, catalog/multi_branch_*, aggregate/
nest_tensor) with "Tried reading schema message, was null or length 0" on
the worker side. The CollectDictionaries + GetDictionaryPayload loop fixes
that; the full integration suite (178 cases, 7670 assertions) passes again.
Verified byte-identical output against the old MakeStreamWriter path on
BIGINT, string, multi-column, empty-struct, list, struct-of-int+string, and
dictionary-of-int8+utf8 schemas.
Measured win (mimalloc pool, 15-iter steady state, python+subprocess):
aggregate_sum t=1, 100k rows +5.3%
aggregate_sum t=1, 1M rows +3.3%
aggregate_sum t=4, 100k rows +5.0%
aggregate_sum t=4, 1M rows +1.9%
table_in_out_sum_all t=4, 100k rows +13.2%
table_in_out_sum_all t=4, 1M rows +11.8%
table_in_out_echo t=4, 1M rows, 1KB payload +4.0%
(others within +-2% noise floor)
The table_in_out_sum_all numbers are the headline: that function calls
SerializeToIpcBytes per input batch on every duckdb worker thread, so
eliminating one Assemble pass plus the growing-buffer realloc chain
compounds across threads. Cold paths (bind, init) also benefit but their
share of the workload is negligible.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>1 parent 195fb13 commit 4827725
1 file changed
Lines changed: 70 additions & 20 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
257 | 257 | | |
258 | 258 | | |
259 | 259 | | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | | - | |
270 | | - | |
271 | | - | |
272 | | - | |
273 | | - | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
274 | 319 | | |
275 | | - | |
276 | | - | |
277 | | - | |
278 | | - | |
279 | | - | |
280 | | - | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
281 | 331 | | |
282 | 332 | | |
283 | 333 | | |
| |||
0 commit comments