Skip to content

Do not zero-fill the kvikio_ofstream staging buffer - #2749

Open
bdice wants to merge 2 commits into
NVIDIA:mainfrom
bdice:kvikio-ofstream-no-zero-fill
Open

bdice wants to merge 2 commits into
NVIDIA:mainfrom
bdice:kvikio-ofstream-no-zero-fill

Conversation

@bdice

@bdice bdice commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

kvikio_ofstream allocated its staging buffer (32 MiB by default) as a std::vector<char>, which value-initializes it, so every open memset the whole buffer. With glibc a 32 MiB request is always a fresh mmap, so the memset faults in all 8,192 pages: about 10 ms per index save, even when the file only receives a few KiB of header and the bulk data goes straight to kvikio (write_device, or host blocks at least as large as the buffer).

This PR allocates the buffer with std::make_unique_for_overwrite<char[]> and keeps its size in a member. Only cpp/src/util/file_io.cpp changes; the public API is unchanged.

  • Same output. Only [pbase(), pptr()) is ever handed to kvikio, and xsputn / overflow write those bytes before advancing pptr(), so the bytes written are identical. A differential test of the old and new sbuf produced byte-identical files, including with the new buffer pre-filled with garbage under ASan/UBSan.
  • Same errors. Open and allocation failures still raise Cannot open file ... for writing.
  • Never slower. When the whole buffer is used, both versions fault the same pages.

A CPU microbenchmark of a 32 MiB allocation with a 4 KiB payload goes from ~10–12 ms to ~0.005 ms per open (8,193 → 2 minor page faults).

Testing

UTIL_TEST (including the FileIO.KvikioOfstream* tests) and the full ctest suite pass.

Measurements

Single-process wall time of each test executable on an RTX 6000 Ada (48 GB), otherwise idle (no ctest parallelism). The old and new libcuvs.so were run alternately, 2 or more repetitions each, with the order reversed between repetitions; the table shows the mean. The two builds differ only by this change and the test binaries are identical. All tests passed in every run.

executable before after change
NEIGHBORS_ANN_VAMANA_TEST 316.3 s 283.8 s -10.3%
NEIGHBORS_ANN_CAGRA_FLOAT_UINT32_TEST 88.5 s 84.2 s -4.9%
NEIGHBORS_ANN_CAGRA_HALF_UINT32_TEST 51.5 s 47.9 s -7.0%
NEIGHBORS_ANN_CAGRA_INT8_UINT32_TEST 74.5 s 71.6 s -3.8%
NEIGHBORS_ANN_CAGRA_UINT8_UINT32_TEST 79.3 s 74.3 s -6.2%
NEIGHBORS_ANN_HNSW_ACE_FLOAT_UINT32_TEST 7.8 s 7.6 s -3.3%
NEIGHBORS_ANN_HNSW_ACE_HALF_UINT32_TEST 10.5 s 10.2 s -2.8%
NEIGHBORS_ANN_HNSW_ACE_INT8_UINT32_TEST 8.6 s 7.4 s -13.4%
NEIGHBORS_ANN_HNSW_ACE_UINT8_UINT32_TEST 7.6 s 7.6 s -0.7%
NEIGHBORS_ANN_CAGRA_BBQ_UINT32_TEST (not affected) 3.8 s 3.9 s +2.4%
NEIGHBORS_ANN_BRUTE_FORCE_TEST 5.1 s 4.0 s -20.2%
NEIGHBORS_ANN_IVF_SQ_TEST 23.4 s 22.3 s -5.0%
NEIGHBORS_ANN_IVF_RABITQ_TEST 11.8 s 10.4 s -12.1%
UTIL_TEST 1.4 s 1.4 s -2.5%
total 690.2 s 636.5 s -7.8%

@bdice bdice added improvement Improves an existing functionality non-breaking Introduces a non-breaking change labels Oct 6, 2026
@copy-pr-bot

copy-pr-bot Bot commented Oct 6, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@julianmi julianmi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, @bdice. LGTM.

@bdice
bdice marked this pull request as ready for review October 7, 2026 16:05
@bdice
bdice requested a review from a team as a code owner October 7, 2026 16:05
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: NVIDIA/cuvs/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Enterprise
  • Run ID: 889b16c3-a0e0-4292-84dc-bb77e907a077
📥 Commits

Reviewing files that changed from the base of the PR and between 7f21ade and a7557dc.

📒 Files selected for processing (1)
  • cpp/src/util/file_io.cpp

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 8 remain after this review.


📝 Summary

Summary by CodeRabbit

  • Performance
    • File output now manages temporary write buffers more efficiently, including for larger writes. Existing file-writing behavior remains unchanged.

Walkthrough

The stream buffer now allocates staging storage with std::make_unique_for_overwrite and tracks its capacity separately. Initialization, direct-write checks, and flush resets use the tracked capacity and allocated pointer.

Changes

Stream Buffer Allocation

Layer / File(s) Summary
Allocate and use the staging buffer
cpp/src/util/file_io.cpp
The staging storage changes from std::vector<char> to an uninitialized std::unique_ptr<char[]> buffer with a separate capacity. Setup, direct-write checks, and flush resets use that capacity and the allocated pointer.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Refactor

Suggested reviewers: julianmi

Merge Risk: ⚪ Minimal · up to a7557

The change avoids zero-initializing the staging buffer while preserving the described write behavior. No merge-blocking issue was established; the PR is mergeable after normal checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: removing unnecessary zero-filling of the kvikio_ofstream staging buffer.
Description check ✅ Passed The description directly explains the staging-buffer change, its performance impact, compatibility, and test results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improves an existing functionality non-breaking Introduces a non-breaking change

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants