Skip to content

nixlbench: fix stats computation and memory management bugs - #1548

Closed
oliviergaraud wants to merge 4 commits into
ai-dynamo:mainfrom
scality:nixlbench_fixes
Closed

oliviergaraud wants to merge 4 commits into
ai-dynamo:mainfrom
scality:nixlbench_fixes

Conversation

@oliviergaraud

@oliviergaraud oliviergaraud commented Apr 17, 2026 •

Copy link
Copy Markdown
Contributor

What?

Fix two bugs in nixlbench:

  1. Incorrect iteration counts in printStats: Throughput was computed
    using per-thread iterations instead of total iterations (all threads
    combined), and latency used total iterations instead of per-thread
    iterations. Since wall time covers all threads running in parallel,
    throughput needs the total count, while latency (per-thread sequential)
    needs the per-thread count.

  2. Memory management issues in deallocateMemory/allocateMemory:

    • deallocateMemory deregistered IOVs in the wrong order — remote IOVs
      (OBJ/BLK/FILE) must be deregistered before local IOVs, and for OBJ
      the deregistration must happen before cleanup to avoid use-after-free.
    • memset in allocateMemory used buffer_size instead of iov.len,
      which could be wrong when the actual allocation size differs.

Open with Devin

Summary by CodeRabbit

  • Bug Fixes

    • Corrected benchmark metric calculations to use per-thread iteration counts so latency and throughput are normalized accurately.
    • Fixed memory initialization to use each segment’s actual size, preventing incorrect initialization and improving measurement fidelity.
  • Refactor

    • Reordered and deferred resource cleanup and deregistration for a more reliable, deterministic teardown and reduced risk of premature cleanup.

… printStats

Throughput needs total iterations (all threads combined) since
wall_time covers all threads running in parallel.  Latency needs
per-thread iterations since each thread runs that many sequentially.

Signed-off-by: Olivier Garaud <olivier.garaud@scality.com>
Two bugs fixed:
- deallocateMemory: deregister remote IOVs (OBJ/BLK/FILE) before local
  IOVs, and for OBJ deregister before cleanup to avoid use-after-free
- memset in allocateMemory: use iov.len instead of buffer_size, which
  could be wrong when the actual allocation size differs

Signed-off-by: Olivier Garaud <olivier.garaud@scality.com>
@copy-pr-bot

copy-pr-bot Bot commented Apr 17, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

Copy link
Copy Markdown

👋 Hi oliviergaraud! Thank you for contributing to ai-dynamo/nixl.

Your PR reviewers will review your contribution then trigger the CI to test your changes.

🚀

@coderabbitai

coderabbitai Bot commented Apr 17, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a413fc07-cd95-4d07-b0d8-3d46fc4966c6

📥 Commits

Reviewing files that changed from the base of the PR and between 478d9f5 and fdbd9e5.

📒 Files selected for processing (1)
  • benchmark/nixlbench/src/worker/nixl/nixl_worker.cpp

📝 Walkthrough

Walkthrough

Compute metrics using per-thread iteration counts and adjust large-block scaling; initialize DRAM buffers using each I/O vector's length; defer local segment cleanup until after remote/backend deregistration and perform backend-specific cleanup ordering.

Changes

Cohort / File(s) Summary
Metrics Computation
benchmark/nixlbench/src/utils/utils.cpp
xferBenchUtils::printStats now derives per_thread_iter = total_iter / num_threads, applies large-block scaling to both total_iter and per_thread_iter, uses total_iter for data transferred and per_thread_iter for latency averaging.
Worker Memory Management
benchmark/nixlbench/src/worker/nixl/nixl_worker.cpp
allocateMemory uses each xferBenchIOV::iov.len for DRAM memset. deallocateMemory defers local iov_lists deregistration/cleanup until after remote/backend cleanup/deregister; remote cleanup sequences now call backend-specific cleanupBasicDesc* after deregister, and final local cleanup still switches on seg_type.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Worker
    participant RemoteBackend
    participant LocalSegments

    Worker->>LocalSegments: allocateMemory(iov_list)
    Note right of LocalSegments: DRAM memset using each `iov.len`
    Worker->>RemoteBackend: begin remote deregister/cleanup
    RemoteBackend-->>Worker: remote cleanup complete (object/block/file backends run cleanupBasicDesc*)
    Worker->>LocalSegments: deregister/cleanup local iov_lists
    LocalSegments-->>Worker: local cleanup complete (cleanupBasicDescDram/cleanupBasicDescVram)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped through loops and counted threads,

Per-ivars warmed where each buffer spreads,
I waited on remotes to finish their tune,
Then tidied my burrows by afternoon,
A little rabbit hums — the bench is set.

🚥 Pre-merge checks | ✅ 2 | ❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main changes: two bug fixes in nixlbench regarding stats computation and memory management.
Description check ✅ Passed The description follows the template with clear 'What?' section explaining both bugs. The 'Why?' justification is integrated with the technical explanation of why iteration counts must be distributed differently.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

devin-ai-integration[bot]

This comment was marked as resolved.

…allocateMemory

Deregister memory before closing file descriptors in the FILE_SEG path,
matching the fix already applied to OBJ_SEG. Backends like GDS or POSIX
may need the FD open during deregistration.

Signed-off-by: Olivier Garaud <olivier.garaud@scality.com>

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
benchmark/nixlbench/src/worker/nixl/nixl_worker.cpp (1)

1003-1011: 🧹 Nitpick | 🔵 Trivial

Nit: GUSLI branch keeps the old cleanup-before-deregister order.

For consistency with the new OBJ/FILE pattern, consider flipping to deregister-then-cleanup here too. Currently safe only because cleanupBasicDescBlk is a no-op (L717–720); if it ever grows a body, this branch will silently regress to the bug this PR is fixing.

♻️ Proposed reorder for consistency
     } else if (xferBenchConfig::backend == XFERBENCH_BACKEND_GUSLI) {
         for (auto &iov_list : remote_iovs) {
-            for (auto &iov : iov_list) {
-                cleanupBasicDescBlk(iov);
-            }
             nixl_reg_dlist_t desc_list(BLK_SEG);
             iovListToNixlRegDlist(iov_list, desc_list);
             CHECK_NIXL_ERROR(agent->deregisterMem(desc_list, &opt_args), "deregisterMem failed");
+            for (auto &iov : iov_list) {
+                cleanupBasicDescBlk(iov);
+            }
         }
     } else if (xferBenchConfig::isStorageBackend()) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@benchmark/nixlbench/src/worker/nixl/nixl_worker.cpp` around lines 1003 -
1011, GUSLI branch currently calls cleanupBasicDescBlk before deregistering,
which can regress if cleanupBasicDescBlk gains behavior; change the loop over
remote_iovs to first build desc_list via iovListToNixlRegDlist and call
agent->deregisterMem(desc_list, &opt_args) (use CHECK_NIXL_ERROR) and only after
successful deregistration call cleanupBasicDescBlk for each iov; update the
block handling XFERBENCH_BACKEND_GUSLI to match the deregister-then-cleanup
order used in OBJ/FILE paths and reference remote_iovs, iovListToNixlRegDlist,
deregisterMem, and cleanupBasicDescBlk when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@benchmark/nixlbench/src/worker/nixl/nixl_worker.cpp`:
- Around line 1003-1011: GUSLI branch currently calls cleanupBasicDescBlk before
deregistering, which can regress if cleanupBasicDescBlk gains behavior; change
the loop over remote_iovs to first build desc_list via iovListToNixlRegDlist and
call agent->deregisterMem(desc_list, &opt_args) (use CHECK_NIXL_ERROR) and only
after successful deregistration call cleanupBasicDescBlk for each iov; update
the block handling XFERBENCH_BACKEND_GUSLI to match the deregister-then-cleanup
order used in OBJ/FILE paths and reference remote_iovs, iovListToNixlRegDlist,
deregisterMem, and cleanupBasicDescBlk when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ed372933-0d39-4891-935b-dc13db6c112b

📥 Commits

Reviewing files that changed from the base of the PR and between 3fe85d2 and 478d9f5.

📒 Files selected for processing (1)
  • benchmark/nixlbench/src/worker/nixl/nixl_worker.cpp

…ocateMemory

Signed-off-by: Olivier Garaud <olivier.garaud@scality.com>

@ofer ofer 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.

The allocation in the nixl_worker is more elegant than the original, but the original was not a bug as in this loop the allocation was to buffer_size anyways.
I would suggest separating the PR into two as well, one for the memory management and one for the statistic calculation change.

@oliviergaraud

oliviergaraud commented Apr 27, 2026 •

Copy link
Copy Markdown
Contributor Author

I would suggest separating the PR into two as well, one for the memory management and one for the statistic calculation change.

@ofer Done: see #1588 and #1590. Should I close this PR ?

@ofer

ofer commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

I would suggest separating the PR into two as well, one for the memory management and one for the statistic calculation change.

@ofer Done: see #1588 and #1590. Should I close this PR ?

Yes, thank you

@oliviergaraud
oliviergaraud deleted the nixlbench_fixes branch May 13, 2026 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants