Skip to content

feat: multi-GPU component placement (DiT / 5Hz LM on separate GPUs) for Gradio + CLI - #1269

Open
Doud-FR wants to merge 5 commits into
ace-step:mainfrom
Doud-FR:feat/multi-gpu-component-placement
Open

feat: multi-GPU component placement (DiT / 5Hz LM on separate GPUs) for Gradio + CLI#1269
Doud-FR wants to merge 5 commits into
ace-step:mainfrom
Doud-FR:feat/multi-gpu-component-placement

Conversation

@Doud-FR

@Doud-FR Doud-FR commented Jul 18, 2026

Copy link
Copy Markdown

Summary

Enables multi-GPU component placement for the Gradio UI and command-line generation path, so the DiT and the 5Hz LM can run on separate GPUs.

On dual mid-range cards (e.g. 2×16 GB) this makes it possible to run the XL DiT + 4B LM together at full diffusion speed — a setup that otherwise needs a single ≥24 GB card, or heavy CPU offload of the DiT (which cripples diffusion throughput and times out on long tracks).

Builds on the great groundwork in #1149 by @imsarang (the ComponentDeviceMap concept and initialize_service wiring, which covered the API-server path). This PR extends it to the Gradio/CLI generation path — where the LM was still initialized on the DiT's device — and adds the pieces needed for real asymmetric multi-GPU rigs.

What's included

  • device_mapping.py — component→device mapping with explicit env overrides (ACESTEP_DIT_DEVICE, ACESTEP_VAE_DEVICE, ACESTEP_LM_DEVICE) for deterministic pinning, on top of the free-VRAM auto-ranking.
  • init_service_orchestrator.py — place DiT / VAE / text_encoder on the mapped devices.
  • acestep_v15_pipeline.py & service_init.py — route the 5Hz LM to its mapped device in the CLI and Gradio init paths, and keep it resident on its dedicated card (no CPU offload) even when the DiT handler offloads VAE/text_encoder to free the DiT's VRAM.
  • llm_inference.py — recognize cuda:N (not just "cuda") when picking the LM dtype, and load the PyTorch LM directly in bf16 instead of loading fp32 then converting — this removes a transient 2× VRAM peak that OOMs a dedicated 16 GB card for the 4B LM.

Example config (2×16 GB: RTX 5080 + RTX 4060 Ti)

ACESTEP_DIT_DEVICE=cuda:0 # DiT + VAE + text_encoder on the fast card
ACESTEP_VAE_DEVICE=cuda:0
ACESTEP_LM_DEVICE=cuda:1 # 4B LM resident on the second card
MAX_CUDA_VRAM=24 # unlock the 4B LM in the tier logic
launch flags: --offload_to_cpu true --offload_dit_to_cpu false --backend pt

Tested

RTX 5080 (16 GB) + RTX 4060 Ti (16 GB), Windows, torch 2.7.1+cu128:

  • initialize_service places DiT on cuda:0, 4B LM (bf16) on cuda:1 — no OOM.
  • Full generations up to 5 minutes (300 s): peak ~10.7 GB on the DiT card, diffusion ~0.18 s/step (60 s) to ~0.9 s/step (300 s).
  • Before this PR, the 4B LM on a single 16 GB card required offload_dit_to_cpu, which slowed diffusion to ~35–52 s/step and timed out on long tracks.

Notes

  • The LM uses the pt backend on its dedicated card (reliable device placement). Making nano-vLLM honor a non-default CUDA device would further speed the LM phase — left for a follow-up.
  • If issue-fix-426: Multi-GPU support by component offloading #1149 lands first, this can be rebased to drop the overlapping device_mapping.py / orchestrator bits and keep only the Gradio-path + dtype fixes.

Summary by CodeRabbit

  • New Features
    • Deterministic multi-GPU component placement for the main model, VAE, and language model, with environment-variable overrides and UI GPU hinting.
  • Bug Fixes
    • Improved multi-GPU language-model initialization by honoring the mapped LM device and skipping unnecessary CPU offload/downgrade when DiT and LM are on different GPUs.
    • Fixed dtype selection and attention-backend selection for indexed CUDA devices (e.g., cuda:1).
  • Tests
    • Added unit tests covering override precedence, VRAM-based ranking, validation, hint formatting, and CUDA-unavailable behavior.

Copilot AI review requested due to automatic review settings July 18, 2026 10:56
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 03fcd820-84e9-4440-8695-54f368405d0d

📥 Commits

Reviewing files that changed from the base of the PR and between d04360f and 0b46765.

📒 Files selected for processing (2)
  • acestep/core/generation/handler/init_service_loader.py
  • acestep/core/generation/handler/init_service_orchestrator.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • acestep/core/generation/handler/init_service_orchestrator.py

📝 Walkthrough

Walkthrough

The changes add per-component device mapping with environment overrides and free-VRAM ranking, apply DiT/VAE placement during service initialization, and route LM initialization through mapped devices with adjusted offload and indexed-device dtype handling.

Changes

Component device placement and LM initialization

Layer / File(s) Summary
Device mapping resolution and validation
acestep/core/generation/device_mapping.py, acestep/core/generation/device_mapping_test.py
Adds immutable DiT/VAE/LM mappings, environment overrides, free-VRAM ranking, UI hint formatting, CUDA validation, and coverage for these behaviors.
Component-aware service orchestration
acestep/core/generation/handler/init_service_orchestrator.py, acestep/core/generation/handler/init_service_loader.py
Service initialization resolves and validates component devices, loads components on their assigned devices, initializes MLX with the DiT device, records the mapping, and recognizes indexed CUDA devices.
LM device and dtype initialization
acestep/acestep_v15_pipeline.py, acestep/ui/gradio/events/generation/service_init.py, acestep/llm_inference.py
LM placement changes offload and downgrade heuristics, selects the mapped LM device, disables offload when separated from DiT, and supports indexed CUDA/XPU dtype detection and explicit model-loading kwargs.

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

Sequence Diagram(s)

sequenceDiagram
  participant ServiceInit
  participant DeviceMapping
  participant InitServiceOrchestrator
  participant LLMHandler
  ServiceInit->>DeviceMapping: resolve LM device and offload setting
  InitServiceOrchestrator->>DeviceMapping: resolve and validate component devices
  InitServiceOrchestrator->>InitServiceOrchestrator: load DiT, VAE, and text encoder on mapped devices
  ServiceInit->>LLMHandler: initialize LM with mapped device
  LLMHandler-->>ServiceInit: complete LM initialization
Loading

Possibly related PRs

Suggested reviewers: chuxij

Poem

I pinned the LM where GPUs gleam,
Split DiT and VAE through a CUDA dream.
No needless offload, devices align,
Dtypes now follow each indexed line.
Hop, hop—initialization’s bright! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: multi-GPU placement of DiT and the 5Hz LM for both Gradio and CLI paths.
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.
✨ 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.

Copilot AI 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.

Pull request overview

This PR extends ACE-Step’s multi-GPU “component placement” support into the Gradio and CLI initialization paths so the DiT and 5Hz LM can be pinned to different GPUs (via env overrides and/or auto-ranking), reducing VRAM contention and enabling larger combined configurations on dual-GPU rigs.

Changes:

  • Adds a new component→device mapping utility with env overrides for deterministic placement.
  • Routes DiT/VAE/text-encoder and the 5Hz LM to potentially different devices in handler + Gradio/CLI init paths.
  • Adjusts LM initialization to better handle cuda:N device strings and (intended) direct bf16 loading to avoid transient VRAM spikes.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
acestep/ui/gradio/events/generation/service_init.py Uses component device map for LM placement during Gradio init and adjusts LM CPU offload behavior.
acestep/llm_inference.py Updates PyTorch LM load path and dtype selection for device strings like cuda:N.
acestep/core/generation/handler/init_service_orchestrator.py Adds per-component device placement (DiT/VAE) during service initialization.
acestep/core/generation/device_mapping.py New utility module: ranks GPUs by free VRAM, supports env overrides, and validates cuda:N indices.
acestep/acestep_v15_pipeline.py CLI path: disables single-GPU LM offload/downgrade heuristics in multi-GPU LM setups and initializes LM on mapped device.

Comment thread acestep/llm_inference.py
Comment thread acestep/ui/gradio/events/generation/service_init.py Outdated
Comment thread acestep/ui/gradio/events/generation/service_init.py Outdated
Comment thread acestep/core/generation/handler/init_service_orchestrator.py
Comment thread acestep/acestep_v15_pipeline.py Outdated
Comment thread acestep/core/generation/device_mapping.py

@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: 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 `@acestep/ui/gradio/events/generation/service_init.py`:
- Around line 144-150: Cache the initial device map before the init_llm block
and reuse it after dit_handler.initialize_service() instead of calling
resolve_component_device_map() again. In
acestep/ui/gradio/events/generation/service_init.py lines 92-106, initialize
_dev_map = None before the if init_llm block; in lines 144-150, compute
_multi_gpu_lm from _dev_map with a None guard, preserving the existing
_lm_offload behavior.
🪄 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

Run ID: 6fa7b41d-bb9b-4d3a-b17a-dc728ab0b432

📥 Commits

Reviewing files that changed from the base of the PR and between 1d5d108 and 047315d.

📒 Files selected for processing (4)
  • acestep/acestep_v15_pipeline.py
  • acestep/core/generation/device_mapping_test.py
  • acestep/core/generation/handler/init_service_orchestrator.py
  • acestep/ui/gradio/events/generation/service_init.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • acestep/acestep_v15_pipeline.py
  • acestep/core/generation/handler/init_service_orchestrator.py

Comment thread acestep/ui/gradio/events/generation/service_init.py

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (2)

acestep/core/generation/handler/init_service_orchestrator.py:84

  • resolve_component_device_map() is applied even when the caller explicitly requested a non-CUDA device (e.g. device="cpu"). On CUDA hosts this will still produce a cuda:N mapping and override the user’s choice, forcing GPU initialization unexpectedly.
            resolved_device = self._resolve_initialize_device(device)
            if component_device_map is None:
                component_device_map = resolve_component_device_map()
            validate_component_device_map(component_device_map)
            dit_device = component_device_map.dit or resolved_device

acestep/acestep_v15_pipeline.py:435

  • The CLI computes _multi_gpu_lm from the CUDA component map unconditionally. On CUDA hosts where the user selected a non-CUDA --device (e.g. cpu/mps/xpu), resolve_component_device_map() will still return cuda:N entries and can incorrectly disable the CPU offload/downgrade heuristics.
    from acestep.core.generation.device_mapping import resolve_component_device_map as _rcdm
    _cmap = _rcdm()
    _multi_gpu_lm = bool(_cmap.dit and _cmap.lm and _cmap.lm != _cmap.dit)

Comment thread acestep/acestep_v15_pipeline.py Outdated
Comment thread acestep/ui/gradio/events/generation/service_init.py Outdated
Comment thread acestep/core/generation/handler/init_service_orchestrator.py
Comment thread acestep/core/generation/handler/init_service_orchestrator.py

@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: 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 `@acestep/core/generation/handler/init_service_loader.py`:
- Line 146: Update the CUDA branch in the device-selection logic to parse the
index from the mapped device string when it uses the cuda:N form, then pass that
index to gpu_config.cuda_supports_bfloat16(). Preserve the existing behavior for
the unindexed cuda device.
🪄 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

Run ID: a528d601-1d7e-4273-9d76-1cf3c2734f52

📥 Commits

Reviewing files that changed from the base of the PR and between 4ae6eae and d04360f.

📒 Files selected for processing (4)
  • acestep/acestep_v15_pipeline.py
  • acestep/core/generation/handler/init_service_loader.py
  • acestep/core/generation/handler/init_service_orchestrator.py
  • acestep/ui/gradio/events/generation/service_init.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • acestep/ui/gradio/events/generation/service_init.py
  • acestep/core/generation/handler/init_service_orchestrator.py
  • acestep/acestep_v15_pipeline.py

Comment thread acestep/core/generation/handler/init_service_loader.py Outdated
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.

2 participants