Skip to content

Bound CUDA PUC chunk size to available GPU memory - #9

Merged
jjschirle merged 1 commit into
masterfrom
fix/gpu-oom-large-bin-counts
Sep 2, 2026
Merged

jjschirle merged 1 commit into
masterfrom
fix/gpu-oom-large-bin-counts

Conversation

@aarmey

@aarmey aarmey commented Sep 2, 2026

Copy link
Copy Markdown
Member

Problem

bayesian_blocks (the default discretizer) has no upper bound on the number of bins it selects. On datasets with many samples — e.g. single-cell data with hundreds of thousands of cells, exactly the use case NetworkInference/PIDC targets and that GPU acceleration is meant to make tractable — it can pick thousands of bins per gene.

compute_puc_full_cuda's intermediate buffers scale as:

  • counts_chunk_gpu: k_bins^2 * num_nodes * chunk_size (Int32)
  • si_chunk_gpu: k_bins * num_nodes * chunk_size (Float64)

so the previous fixed chunk_size = 256 could request far more GPU memory than exists, even for small gene panels. Confirmed empirically: infer_network with the CUDA backend on a 43-gene x 265,188-sample matrix (continuous, mostly-unique log-normalized single-cell values) triggered:

ERROR: LoadError: Out of GPU memory trying to allocate 117.123 GiB
Effective GPU memory usage: 70.94% (16.683 GiB/23.516 GiB)
...
 [11] compute_puc_full_cuda(nodes::Vector{Node}, config::PIDCConfig, base::Int64)
    @ FastPIDCCUDAExt ~/.julia/packages/FastPIDC/kNsZf/ext/FastPIDCCUDAExt/FastPIDCCUDAExt.jl:193

(bayesian_blocks had picked k_bins ≈ 1690 for this data.) Repository issues are disabled, so opening this directly as a PR rather than filing a report first.

Fix

Size the chunk to fit the GPU memory actually free at call time (CUDA.memory_info()), clamped to the previous default of at most 256, with an 0.8 safety factor for the fixed-size buffers (data_gpu, marginals_gpu, puc_scores_gpu, mi_matrix_gpu) and allocator overhead. If even a single-gene chunk (chunk_size = 1) wouldn't fit, raise a clear ErrorException naming the cause (large k_bins from an adaptive discretizer) and suggesting remedies (uniform_width with a fixed bin count, or config.backend = :cpu), instead of surfacing a raw CUDA OOM stacktrace.

No public API changes — compute_puc_full_cuda's signature and behavior for well-behaved (small k_bins) inputs are unchanged; verified below.

Testing

  • test/cuda_numeric_tests.jl's CPU/GPU equivalence, symmetry, and determinism checks pass unchanged on toy_small_200.txt (small k_bins, so chunk_size still resolves to the old default of 200/256 there — confirmed the sizing logic is a no-op in the well-behaved case).
  • The previously-failing 43-gene/265,188-sample case now completes: k_bins=1690 → auto-selected chunk_size=11 (4 chunk iterations) instead of the 117 GiB allocation attempt.

Suggested follow-ups (not included here, happy to open separately if useful)

  • A hard cap on bayesian_blocks' bin count (or a max_bins keyword) so CPU-backend runs and overall runtime aren't also blown out by very large k_bins.
  • Documenting the k_bins^2 * num_nodes * chunk_size GPU memory scaling and recommending uniform_width explicitly for large-sample-count single-cell inputs in the README/CLI help.

🤖 Generated with Claude Code

https://claude.ai/code/session_0151Ww8ZmYpes58vMJNbXHpA

bayesian_blocks (the default discretizer) has no upper bound on the
number of bins it selects, and on datasets with many samples (e.g.
single-cell data with hundreds of thousands of cells) it can pick
thousands of bins per gene. compute_puc_full_cuda's intermediate
counts_chunk_gpu/si_chunk_gpu buffers scale with k_bins^2 and k_bins
respectively, so the previous fixed chunk_size=256 could request far
more GPU memory than is available even for small gene panels
(confirmed empirically: a 43-gene x 265,188-sample dataset triggered a
117 GiB allocation request).

Size the chunk to fit within the GPU memory actually free at call
time instead, clamped to the previous default of at most 256, and
raise an actionable error (suggesting uniform_width with a fixed bin
count, or the CPU backend) if even a single-gene chunk would not fit,
rather than surfacing a raw CUDA out-of-memory error.

Verified against test/cuda_numeric_tests.jl's CPU/GPU equivalence,
symmetry, and determinism checks (unchanged), and against the
previously-failing 43-gene/265,188-sample bayesian_blocks case (now
completes, auto-selecting chunk_size=11 for k_bins=1690).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0151Ww8ZmYpes58vMJNbXHpA
@aarmey
aarmey requested a review from jjschirle September 2, 2026 04:01
@aarmey aarmey self-assigned this Sep 2, 2026
@aarmey aarmey added the bug Something isn't working label Sep 2, 2026
@jjschirle
jjschirle merged commit 31d1f12 into master Sep 2, 2026
4 checks passed
@aarmey
aarmey deleted the fix/gpu-oom-large-bin-counts branch September 4, 2026 01:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants