Skip to content

test: add cuInit contention benchmark for issue #1662 - #247

Open
chidwipak wants to merge 2 commits into
Project-HAMi:mainfrom
chidwipak:chidwipak/bench/init-contention
Open

test: add cuInit contention benchmark for issue #1662#247
chidwipak wants to merge 2 commits into
Project-HAMi:mainfrom
chidwipak:chidwipak/bench/init-contention

Conversation

@chidwipak

@chidwipak chidwipak commented Aug 1, 2026

Copy link
Copy Markdown

What type of PR is this?

New test infrastructure (benchmark tool)

What this PR does / why we need it:

Adds test/benchmark/bench_init_contention.c and test/benchmark/README.md — a measurement tool for the initialization contention described in Project-HAMi/HAMi#1662.

The program forks N processes that each call cuInit() under LD_PRELOAD=libvgpu.so, synchronized with a two-phase process-shared barrier. Phase 1 collects all processes as ready; the parent records wall_start between the phases; Phase 2 releases all processes together so no child can enter cuInit() before timing begins. It reports per-process latency (min, p50, mean, p95, p99, max), wall time, CPU time per process, and blocked time. A CSV line per run makes it easy to sweep over different process counts.

cuInit() is timed because it calls ensure_post_init() at its tail (src/libvgpu.c:945), which runs postInit() once per process via pthread_once. postInit() acquires sem_postinit through lock_postinit() and calls set_task_pid() under it. set_task_pid() runs nvmlInit(), scans running processes twice, and retains a primary CUDA context. Because sem_postinit serializes this across every process on the node, timing cuInit() end-to-end captures the full contention cost.

The file is picked up automatically by test/CMakeLists.txt (its GLOB_RECURSE covers all *.c files under test/). No build-system changes are needed.

Which issue(s) this PR fixes:

Related to Project-HAMi/HAMi#1662

Special notes for your reviewer:

Tested on a 4-GPU node, Tesla K80, driver 470.256.02, CUDA 11.4 kernel driver, HAMi-core commit 52f33fc, CUDA_VISIBLE_DEVICES=1:

N wall (ms) mean (ms) p99/max (ms) CPU/proc (ms) blocked/proc (ms)
1 166 148 148 126 22
8 1,314 767 1,294 141 626
32 5,293 2,985 5,275 147 2,838
64 10,637 5,884 10,615 147 5,737
128 21,290 11,757 21,265 147 11,610
256 42,678 23,674 42,652 150 23,524

Wall time scales linearly with N. Per-process CPU stays flat at ~150 ms. Blocked time grows linearly. For N=256, the last process waits ~43 seconds, matching the "~60 s startup" report in the issue.

Note: PRs #228 and #229 (currently open) fix set_task_pid() to use the correct device for cuDevicePrimaryCtxRetain. Neither changes the sem_postinit serialization that this benchmark measures.

Note: PR #239 (also open) adds a broader benchmark suite under bench/ with its own Makefile. This PR adds a single focused tool under test/benchmark/, which integrates with the existing test/CMakeLists.txt and needs no separate build configuration.

AI assistance: I used GitHub Copilot for repository navigation and review assistance while building and analyzing this benchmark. The program was designed, built, and run by me on the K80 machine above. Every number in this PR comes from an actual run on that hardware — nothing is estimated. I can explain every part of the implementation and take responsibility for addressing review feedback.

Does this PR introduce a user-facing change?:

No. This adds only test/benchmark/ files. No src/ changes.

Summary by CodeRabbit

  • New Features

    • Added a Linux benchmark for measuring concurrent initialization performance and contention.
    • Reports latency statistics, CPU usage, and context-switch metrics in human-readable and CSV formats.
    • Supports configurable process counts with input validation and failure handling.
  • Documentation

    • Added build and execution instructions, output field descriptions, and Linux/runtime notes for the benchmark.

Fork N processes that each call cuInit() under LD_PRELOAD, synchronized
with a process-shared barrier so they all hit sem_postinit at once.
Reports per-process cuInit() latency and blocked time for N=1..256.

Validated on Tesla K80, driver 470.256.02, commit 52f33fc.

Signed-off-by: chidwipak kuppani <chidwipak@gmail.com>
@hami-robot

hami-robot Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

@chidwipak: The label(s) kind/feature cannot be applied, because the repository doesn't have them.

Details

In response to this:

What type of PR is this?

/kind feature

What this PR does / why we need it:

Adds test/benchmark/bench_init_contention.c and test/benchmark/README.md — a measurement tool for the initialization contention described in Project-HAMi/HAMi#1662.

The program forks N processes that each call cuInit() under LD_PRELOAD=libvgpu.so, synchronized with a process-shared pthread_barrier so they all hit sem_postinit at the same time. It reports per-process latency (min, p50, mean, p95, p99, max), wall time, CPU time per process, and blocked time. A CSV line per run makes it easy to sweep over different process counts.

cuInit() is timed because it calls ensure_post_init() at its tail (src/libvgpu.c:945), which runs postInit() once per process via pthread_once. postInit() acquires sem_postinit through lock_postinit() and calls set_task_pid() under it. set_task_pid() runs nvmlInit(), scans running processes twice, and retains a primary CUDA context. Because sem_postinit serializes this across every process on the node, timing cuInit() end-to-end captures the full contention cost.

The file is picked up automatically by test/CMakeLists.txt (its GLOB_RECURSE covers all *.c files under test/). No build-system changes are needed.

Which issue(s) this PR fixes:

Related to Project-HAMi/HAMi#1662

Special notes for your reviewer:

Tested on a 4-GPU node, Tesla K80, driver 470.256.02, CUDA 11.4 kernel driver, HAMi-core commit 52f33fc, CUDA_VISIBLE_DEVICES=1:

N wall (ms) mean (ms) p99/max (ms) CPU/proc (ms) blocked/proc (ms)
1 166 148 148 126 22
8 1,314 767 1,294 141 626
32 5,293 2,985 5,275 147 2,838
64 10,637 5,884 10,615 147 5,737
128 21,290 11,757 21,265 147 11,610
256 42,678 23,674 42,652 150 23,524

Wall time scales linearly with N. Per-process CPU stays flat at ~150 ms. Blocked time grows linearly. For N=256, the last process waits ~43 seconds, matching the "~60 s startup" report in the issue.

Note: PRs #228 and #229 (currently open) fix set_task_pid() to use the correct device for cuDevicePrimaryCtxRetain. Neither changes the sem_postinit serialization that this benchmark measures.

AI assistance: I used GitHub Copilot for repository navigation and review assistance while building and analyzing this benchmark. The program was designed, built, and run by me on the K80 machine above. Every number in this PR comes from an actual run on that hardware — nothing is estimated. I can explain every part of the implementation and take responsibility for addressing review feedback.

Does this PR introduce a user-facing change?:

No. This adds only test/benchmark/ files. No src/ changes.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@hami-robot

hami-robot Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: chidwipak
Once this PR has been reviewed and has the lgtm label, please assign archlitchi for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@hami-robot

hami-robot Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Welcome @chidwipak! It looks like this is your first PR to Project-HAMi/HAMi-core 🎉

@hami-robot hami-robot Bot added the size/L label Aug 1, 2026
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Linux benchmark for concurrent cuInit() contention. The benchmark synchronizes forked processes, records initialization and CPU metrics, reports aggregate statistics in text and CSV formats, and documents build, execution, and output details.

Changes

Initialization contention benchmark

Layer / File(s) Summary
Benchmark data and statistics
test/benchmark/bench_init_contention.c
Defines shared result and barrier data, then adds timing, sorting, and percentile helpers.
Synchronized process execution
test/benchmark/bench_init_contention.c
Parses arguments, creates shared synchronization state, forks children, measures cuInit(), records results, and cleans up failures and resources.
Result reporting and usage documentation
test/benchmark/bench_init_contention.c, test/benchmark/README.md
Reports latency, CPU, blocking, context-switch, failure, and CSV metrics. Documents build commands, arguments, output fields, and platform requirements.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BenchmarkMain
  participant SharedMemory
  participant ChildProcess
  participant CUDARuntime
  BenchmarkMain->>SharedMemory: initialize barrier and result storage
  BenchmarkMain->>ChildProcess: fork synchronized children
  ChildProcess->>SharedMemory: wait at the process-shared barrier
  ChildProcess->>CUDARuntime: call cuInit()
  CUDARuntime-->>ChildProcess: return status
  ChildProcess->>SharedMemory: record timing and resource metrics
  BenchmarkMain->>SharedMemory: collect child results
  BenchmarkMain-->>BenchmarkMain: print statistics and CSV output
Loading

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: chaunceyjiang, archlitchi

Poem

A rabbit queued each process tight,
At the barrier, still and bright.
cuInit() ran; the clocks took note,
CSV carrots filled the report.
“Hop!” said the bench, “contention’s in sight!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the added cuInit() contention benchmark and matches the main changes.
✨ 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.

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Aug 1, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/benchmark/bench_init_contention.c`:
- Around line 110-115: Update the benchmark startup synchronization around
shared->barrier so children first signal readiness, then the parent records
wall_start between synchronization phases, and finally the existing barrier
releases all children together. Reuse the same barrier for both readiness and
start phases, ensuring no child can enter cuInit() before wall-clock timing
begins.
- Around line 93-100: Update the percentile function’s rank calculation to use
nearest-rank semantics, computing the zero-based index as ceil(p/100 * n) minus
one. Preserve the existing empty-input handling and bounds clamping for
percentile values outside the valid range.
- Around line 205-214: Update the process-count parsing in the benchmark’s main
argument handling to use strtol() instead of atoi(), validate that the entire
argument is consumed and represents a positive integer within MAX_PROC_NUM, and
only then assign proc_num; retain the existing error message and failure return
for invalid input.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 19c5e8bf-cf04-47a4-be2d-a62a266922ad

📥 Commits

Reviewing files that changed from the base of the PR and between 52f33fc and 2476ad3.

📒 Files selected for processing (2)
  • test/benchmark/README.md
  • test/benchmark/bench_init_contention.c

Comment thread test/benchmark/bench_init_contention.c Outdated
Comment thread test/benchmark/bench_init_contention.c Outdated
Comment thread test/benchmark/bench_init_contention.c
Fix three issues flagged by CodeRabbit:
- Percentile: use ceil(p/100*n)-1 index, not floor; fixes off-by-one
  for p50 when N is even (the common case in our sweep)
- Wall timer: record wall_start before releasing children via a second
  barrier phase, so no child can enter cuInit() before timing begins
- Replace atoi() with strtol() to avoid undefined behaviour on overflow

Signed-off-by: chidwipak kuppani <chidwipak@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/benchmark/bench_init_contention.c`:
- Line 210: Update the process-count parsing near strtol in the benchmark
initialization code to use int32_t instead of long, while retaining the
end-pointer validation, range checks, and errno == ERANGE handling; use the
existing strtol/strtol_l flow or an equivalent fixed-width conversion and ensure
the parsed value is safely range-checked before assignment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0e3f891e-5bf6-4492-a187-2ec86854edc2

📥 Commits

Reviewing files that changed from the base of the PR and between 2476ad3 and 5accefc.

📒 Files selected for processing (1)
  • test/benchmark/bench_init_contention.c


if (argc >= 2) {
char *end;
long val = strtol(argv[1], &end, 10);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate file:"
fd -a 'bench_init_contention\.c$' . || true

file="$(fd 'bench_init_contention\.c$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
  echo
  echo "File: $file"
  wc -l "$file"
  echo
  echo "Relevant lines 180-230:"
  sed -n '180,230p' "$file" | cat -n -v
  echo
  echo "Search MAX_PROC_NUM / cpplint / lint config:"
  rg -n "MAX_PROC_NUM|MAX\(|cpplint|lint|STRTO|strtol|strtoll" "$file" -C 2 || true

  echo
  echo "Repository files mentioning cpplint:"
  rg -n "cpplint|lint" -S --glob '!vendor/**' --glob '!third_party/**' --glob '!build/**' . | head -n 80 || true
fi

echo
echo "Check repo local cpplint availability/options:"
(command -v cpplint >/dev/null 2>&1 && cpplint --version; true)

Repository: Project-HAMi/HAMi-core

Length of output: 4169


🌐 Web query:

google style cpplint runtime/int64 long cpplint error

💡 Result:

The cpplint error "runtime/int" (often triggered by the use of long) occurs because the Google C++ Style Guide mandates the use of fixed-width integer types from the C++ standard library, such as int32_t or int64_t, rather than built-in C types like short, long, or long long [1][2]. This rule is enforced to ensure code portability, as the size of built-in types (specifically long) can vary significantly across different compilers and operating systems (e.g., 32-bit on Windows vs. 64-bit on many Unix-like systems) [3]. To resolve or handle this error, you have three primary options: 1. Replace the types: Refactor your code to use the appropriate sized integer types from (e.g., replace long with int64_t or int32_t as appropriate for your application) [2][4]. 2. Suppress the warning for specific lines: You can add a comment to the offending line to instruct cpplint to ignore it [5]: // NOLINTNEXTLINE(runtime/int) long my_variable; 3. Filter out the check: If you decide that this rule does not apply to your project, you can disable the warning globally by running cpplint with a filter flag [2][5]: cpplint --filter=-runtime/int your_file.cpp

Citations:


Use a fixed-width integer for the process count parsing.

cpplint rejects long in runtime/int. Use int32_t with strtol/strtol_l or another fixed-width conversion; keep the end-pointer, range, and errno == ERANGE checks.

🧰 Tools
🪛 GitHub Check: cpplint

[failure] 210-210:
[cpplint] reported by reviewdog 🐶
Use int16_t/int64_t/etc, rather than the C type long [runtime/int] [4]

Raw Output:
test/benchmark/bench_init_contention.c:210: Use int16_t/int64_t/etc, rather than the C type long [runtime/int] [4]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/benchmark/bench_init_contention.c` at line 210, Update the process-count
parsing near strtol in the benchmark initialization code to use int32_t instead
of long, while retaining the end-pointer validation, range checks, and errno ==
ERANGE handling; use the existing strtol/strtol_l flow or an equivalent
fixed-width conversion and ensure the parsed value is safely range-checked
before assignment.

Source: Linters/SAST tools

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant