Skip to content

perf: buffer small writes on InsertStream - #58

Merged
ShawnChen-Sirius merged 1 commit into
chdb-io:mainfrom
hyperbyte-cloud:perf/insert-stream-buffering
Sep 30, 2026
Merged

ShawnChen-Sirius merged 1 commit into
chdb-io:mainfrom
hyperbyte-cloud:perf/insert-stream-buffering

Conversation

@austin-barrington

@austin-barrington austin-barrington commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Writing to an InsertStream through io::Write was sending every write straight to chdb_stream_append, and the engine copies each chunk. So the pattern in examples/14_insert_stream.rs — a writeln! per row — ends up as one FFI call (sometimes a few) per row.

I timed it with 200k rows: ~385ms with plain writeln!, ~37ms with the same thing wrapped in a BufWriter. After this change the plain version is in the same range as the BufWriter one (~50-70ms here).

What changed:

  • Writes through io::Write are gathered into a 64 KiB buffer and sent when it fills, on flush(), or on finish().
  • Writes bigger than the buffer go straight through.
  • append() still sends immediately (it flushes anything buffered first so order is kept).
  • Added with_write_buffer(bytes) to change the size, 0 turns buffering off. DEFAULT_WRITE_BUFFER is exported.

One behaviour note: since writes are buffered, an error from a bad row can now show up on a later write, on flush() or on finish() rather than on the exact write that caused it. Dropping the stream without finish() still cancels it, buffered data included.

Added tests for lots of small writes, a write bigger than the buffer, drop after buffered writes, and different buffer sizes including 0.

Note

Buffer small writes in InsertStream with 64 KiB default threshold

  • std::io::Write calls now copy bytes into an in-memory buffer and submit them to the engine only when the threshold is exceeded; writes at or above the threshold go straight through.
  • Adds InsertStream::with_write_buffer to change or disable the threshold (0 disables), and exposes DEFAULT_WRITE_BUFFER from the crate root.
  • append and finish flush pending buffered bytes before proceeding, preserving write order. A flush failure in finish returns an error without committing; Drop still cancels.
  • Behavioral Change: std::io::Write on InsertStream no longer submits each call immediately by default. Data now reaches the engine in batches — see insert_stream.rs; the zero-buffer option restores immediate submission.

Macroscope summarized 68e5fa2.

Every io::Write call was going straight to chdb_stream_append, and the
engine copies each chunk. With a writeln! per row that is one FFI call
(often more) per row. 200k rows took ~385ms that way vs ~37ms through a
BufWriter.

Writes now get gathered into a 64 KiB buffer and sent when it fills, on
flush, or on finish. append() still sends straight away. The size can be
changed with with_write_buffer(), and 0 turns buffering off.
@ShawnChen-Sirius
ShawnChen-Sirius merged commit c17efbd into chdb-io:main Sep 30, 2026
11 checks passed
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.

2 participants