Skip to content

fix(core): coarse-shard NLL multithread + safe GPU PRIMARY backends - #371

Merged
seonghobae merged 2 commits into
mainfrom
seonghobae/pr290-gpu-mt-paper-audit
Jul 31, 2026
Merged

fix(core): coarse-shard NLL multithread + safe GPU PRIMARY backends#371
seonghobae merged 2 commits into
mainfrom
seonghobae/pr290-gpu-mt-paper-audit

Conversation

@seonghobae

@seonghobae seonghobae commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Paper contract (unchanged)

eta_pi = exp(alpha_i) * theta_p,d(i) + b_i - exp(tau) * r_pi

Verification

  • cargo test --workspace ×2: 731 passed
  • pytest tests/test_rust_parity.py tests/test_objective.py tests/test_backend.py -q ×2: 79 passed
  • Rust backend launch smoke ×2: objective finite, theta (40,2)

Test plan

  • cargo workspace
  • rust/numpy parity suite
  • coarse_shard_multithread closed-form unit test

Summary by CodeRabbit

  • 개선 사항

    • GPU 초기화 호환성을 개선하고, 지원 가능한 환경에서 보다 일관된 GPU 실행을 제공합니다.
    • 대규모 인원 데이터 처리 시 음의 로그우도 및 gradient 계산 성능을 개선했습니다.
    • GPU를 사용할 수 없는 경우 CPU 경로로 안전하게 대체할 수 있습니다.
  • 테스트

    • 대규모 데이터셋에서 목적함수와 gradient 계산의 정확성 및 안정성을 검증하는 테스트를 추가했습니다.

Keep simple-structure MLS2PLM eta algebra while parallelizing the
CPU hot path with fixed person shards (N>=256) and a single gradient
reduce. Route all wgpu modules through PRIMARY backends only so broken
/dev/dri sandboxes soft-fail to f64 CPU instead of SIGSEGV. Pin with a
closed-form large-N unit test.
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@seonghobae, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cc64e873-52f5-4e47-b4b3-48225a564177

📥 Commits

Reviewing files that changed from the base of the PR and between 485ceff and 7f276b5.

📒 Files selected for processing (2)
  • crates/mlsirm-core/src/lib.rs
  • tests/unit/lib_tests.rs
📝 Walkthrough

Walkthrough

이 PR은 GPU 컨텍스트 초기화를 위한 공용 함수 gpu_init::new_instance()를 도입하고, 5개 GPU 모듈에서 기본 wgpu::Instance 생성 대신 이를 사용하도록 변경했다. 또한 lib.rs의 neg_loglik_and_grad 함수에 인원 수 기준 병렬 계산 경로를 추가하고, 관련 테스트를 새로 작성했다.

Changes

GPU 초기화 통합 및 병렬 NLL 계산

Layer / File(s) Summary
공용 GPU 인스턴스 생성 함수 추가
crates/mlsirm-core/src/gpu_init.rs, crates/mlsirm-core/src/lib.rs
GL/EGL을 제외한 제한된 백엔드로 wgpu::Instance를 생성하는 new_instance 함수를 추가했다. 어댑터 부재는 소프트 실패로 처리한다. lib.rs는 GPU 기능 활성화 시 이 모듈을 조건부로 포함한다.
GPU 컨텍스트 초기화 경로 변경
crates/mlsirm-core/src/gpu.rs, gpu_eapsum.rs, gpu_marginal.rs, gpu_plausible.rs, gpu_scoring.rs
각 파일의 GpuContext::init에서 wgpu::Instance::default() 호출을 crate::gpu_init::new_instance() 호출로 대체했다.
인원 기준 coarse-shard 병렬 NLL/gradient 계산
crates/mlsirm-core/src/lib.rs
NLL_MT_PERSON_FLOOR 상수를 추가하고, neg_loglik_and_grad가 인원 수와 가용 병렬성에 따라 단일 스레드 경로와 worker별 구간 병렬 경로를 선택하도록 변경했다. 전체 인원 처리 로직을 neg_loglik_and_grad_range 함수로 분리하고, reduce_nll_partials 함수로 worker별 부분 결과(목적함수, theta/alpha/b/xi/zeta/tau gradient)를 합산한다.
대규모 인원 병렬 경로 검증 테스트
tests/unit/lib_tests.rs
N=300 조건에서 person-shard 병렬 계산 경로를 검증하는 테스트를 추가했다. 목적함수와 gradient의 유한성, objective + loglik = 0 관계, 논문 수식으로 계산한 전체 NLL과의 일치 여부를 확인한다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant neg_loglik_and_grad
  participant Worker1 as neg_loglik_and_grad_range Worker
  participant Worker2 as neg_loglik_and_grad_range Worker
  participant reduce_nll_partials

  Caller->>neg_loglik_and_grad: 인원 수 N, 파라미터 전달
  neg_loglik_and_grad->>neg_loglik_and_grad: N과 NLL_MT_PERSON_FLOOR 비교
  neg_loglik_and_grad->>Worker1: 인원 구간 [start1,end1) 할당
  neg_loglik_and_grad->>Worker2: 인원 구간 [start2,end2) 할당
  Worker1-->>neg_loglik_and_grad: 부분 목적함수/gradient 반환
  Worker2-->>neg_loglik_and_grad: 부분 목적함수/gradient 반환
  neg_loglik_and_grad->>reduce_nll_partials: 부분 결과 목록 전달
  reduce_nll_partials-->>neg_loglik_and_grad: 합산된 목적함수/gradient 반환
  neg_loglik_and_grad-->>Caller: 최종 결과 반환
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 변경 사항은 NLL 멀티스레딩과 GPU 초기화이며, 이슈 #680의 NVIDIA NIM provider 요구사항을 구현하지 않습니다. NVIDIA NIM provider, 모델 후보 우선순위, NVIDIA_API_KEY 건너뛰기 로직, 관련 문서와 테스트를 구현하거나 올바른 이슈를 연결하세요.
Out of Scope Changes check ⚠️ Warning 이슈 #680은 OpenCode용 NVIDIA NIM 설정을 요구하지만, PR은 CPU NLL 처리와 GPU 백엔드를 변경합니다. 이슈 #680과 무관한 CPU NLL 및 GPU 변경을 별도 PR로 분리하거나, 이 변경을 설명하는 올바른 이슈를 연결하세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 coarse-shard NLL 멀티스레딩과 GPU PRIMARY 백엔드 변경을 정확히 요약합니다.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch seonghobae/pr290-gpu-mt-paper-audit

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

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
crates/mlsirm-core/src/lib.rs (1)

466-499: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

theta/xi gradient 병합에서 불필요한 전체 배열 순회가 발생합니다.

thetaxi는 person 인덱스로 분할되므로, 각 worker의 partial에서 자신이 담당한 [start, end) 구간 밖은 항상 0.0입니다. 그런데도 reduce_nll_partials는 모든 worker의 partial에 대해 thetaxi 전체 배열(n_persons * n_dims, n_persons * latent_dim)을 순회하며 더합니다. 이는 O(worker_count * n_persons) 만큼의 불필요한 작업을 만들며, worker 수와 n_persons가 커질수록 병렬화로 얻은 이득을 상쇄할 수 있습니다.

alpha, b, zeta, tau는 item 기준이라 모든 worker가 값을 채울 수 있으므로 전체 reduce가 필요합니다. 하지만 thetaxi는 person 구간(start, end) 정보를 partial과 함께 전달하면, 해당 구간만 복사하는 방식으로 바꿀 수 있습니다.

♻️ 제안하는 개선 방향
-let partials = std::thread::scope(|scope| {
+let partials: Vec<(usize, usize, f64, Gradients)> = std::thread::scope(|scope| {
     let mut handles = Vec::with_capacity(worker_count);
     for worker in 0..worker_count {
         let start = worker * chunk;
         let end = (start + chunk).min(config.n_persons);
         if start >= end {
             continue;
         }
         handles.push(scope.spawn(move || {
-            neg_loglik_and_grad_range(
+            let (obj, g) = neg_loglik_and_grad_range(
                 y, mask, factor_id, params, config,
                 free_alpha, uses_space, gamma, start, end,
-            )
+            );
+            (start, end, obj, g)
         }));
     }
     handles.into_iter().map(|h| h.join().expect("neg_loglik worker panicked")).collect()
 });

-fn reduce_nll_partials(partials: Vec<(f64, Gradients)>, config: &ModelConfig) -> (f64, Gradients) {
+fn reduce_nll_partials(
+    partials: Vec<(usize, usize, f64, Gradients)>,
+    config: &ModelConfig,
+) -> (f64, Gradients) {
     ...
-    for (obj, g) in partials {
+    for (start, end, obj, g) in partials {
         objective += obj;
-        for (dst, src) in grad.theta.iter_mut().zip(&g.theta) {
-            *dst += src;
-        }
+        grad.theta[start * config.n_dims..end * config.n_dims]
+            .copy_from_slice(&g.theta[start * config.n_dims..end * config.n_dims]);
         for (dst, src) in grad.alpha.iter_mut().zip(&g.alpha) {
             *dst += src;
         }
         ...
-        for (dst, src) in grad.xi.iter_mut().zip(&g.xi) {
-            *dst += src;
-        }
+        grad.xi[start * config.latent_dim..end * config.latent_dim]
+            .copy_from_slice(&g.xi[start * config.latent_dim..end * config.latent_dim]);
         ...
     }
🤖 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 `@crates/mlsirm-core/src/lib.rs` around lines 466 - 499, Update
reduce_nll_partials and the partial-result representation to carry each worker’s
person range (start, end) alongside its objective and Gradients. Replace
full-array accumulation for theta and xi with updates limited to that worker’s
assigned range, while preserving full reduction for alpha, b, zeta, and tau and
retaining zero values outside each worker’s range.
🤖 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 `@crates/mlsirm-core/src/lib.rs`:
- Around line 72-73: Apply the GPU cfg attribute directly to the gpu_init module
declaration in lib.rs, ensuring gpu_init is compiled only when feature "gpu" is
enabled and coverage is disabled. Preserve the existing cfg behavior for the
following item and avoid leaving mod gpu_init unconditionally compiled.

In `@tests/unit/lib_tests.rs`:
- Around line 286-294: Update the test around neg_loglik_and_grad to force a
worker count of at least two, then evaluate the same fixture through both the
single-threaded and parallel paths. Compare objective, loglik, and every
gradient component (tau, theta, alpha, b, xi, and zeta) within appropriate
tolerances, replacing the finite-only assertions while preserving the existing
zero-penalty relationship check.

---

Nitpick comments:
In `@crates/mlsirm-core/src/lib.rs`:
- Around line 466-499: Update reduce_nll_partials and the partial-result
representation to carry each worker’s person range (start, end) alongside its
objective and Gradients. Replace full-array accumulation for theta and xi with
updates limited to that worker’s assigned range, while preserving full reduction
for alpha, b, zeta, and tau and retaining zero values outside each worker’s
range.
🪄 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: 6c938d11-83a1-4436-83a4-a7b7f9f112f6

📥 Commits

Reviewing files that changed from the base of the PR and between a3123a2 and 485ceff.

📒 Files selected for processing (8)
  • crates/mlsirm-core/src/gpu.rs
  • crates/mlsirm-core/src/gpu_eapsum.rs
  • crates/mlsirm-core/src/gpu_init.rs
  • crates/mlsirm-core/src/gpu_marginal.rs
  • crates/mlsirm-core/src/gpu_plausible.rs
  • crates/mlsirm-core/src/gpu_scoring.rs
  • crates/mlsirm-core/src/lib.rs
  • tests/unit/lib_tests.rs

Comment thread crates/mlsirm-core/src/lib.rs
Comment thread tests/unit/lib_tests.rs Outdated
Expose neg_loglik_and_grad_with_workers so tests force workers=4 and
bit-compare objective/loglik/all gradient blocks against the single-thread
path. Reorder gpu_init with an explicit per-mod cfg comment so feature
gating cannot be mis-read.
@seonghobae
seonghobae merged commit aa61b57 into main Jul 31, 2026
33 checks passed
@seonghobae
seonghobae deleted the seonghobae/pr290-gpu-mt-paper-audit branch July 31, 2026 11:12
seonghobae added a commit that referenced this pull request Jul 31, 2026
Ship the PR #371 performance/reliability fix as a patch release: person-shard
Rust multithreading for the NLL hot path and PRIMARY-only wgpu backends with
CPU fallback. Changelog notes paper-contract confirmation.
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.

1 participant