Skip to content

fix(training): select XLAStrategy for multi-device XLA/TPU training - #1427

Merged
Borda merged 3 commits into
roboflow:developfrom
JESUSROYETH:fix/xla-multi-device-strategy
Sep 4, 2026
Merged

Borda merged 3 commits into
roboflow:developfrom
JESUSROYETH:fix/xla-multi-device-strategy

Conversation

@JESUSROYETH

Copy link
Copy Markdown
Contributor

Summary

Asking for more than one TPU chip fails before any accelerator work happens:

ValueError: The `XLAAccelerator` can only be used with a `SingleDeviceXLAStrategy`
or `XLAStrategy`, found DDPStrategy.

XLAAccelerator pairs only with SingleDeviceXLAStrategy or XLAStrategy. But build_trainer's own strategy branch turns strategy="auto" into DDPStrategy(find_unused_parameters=True) as soon as more than one device is requested:

elif strategy_name == "ddp" or (strategy_name == "auto" and distributed_requested):
    ...
    strategy = _DDPStrategy(find_unused_parameters=True)

TrainConfig.devices defaults to 1, a default TPU run never reaches that branch and works fine. But if you ask for the whole slice, the run dies during Trainer construction. Since it fails before touching a chip, it looks like a configuration mistake and not a missing feature, and the one-word workaround (strategy="xla") is nowhere in the error message.

This change selects "xla" when the accelerator resolves to XLA/TPU, the strategy is still "auto", and more than one device or node is requested. It's the same kind of gap precision already needed handling for on this path: XLA needs its own object, the generic default doesn't work.

Two more paths hit the same crash and need the same fix:

  • accelerator="auto", which is TrainConfig.accelerator's own default. build_trainer decides the strategy before Trainer runs its own accelerator resolution, so checking only for the literal strings "xla"/"tpu" misses the case where the caller never names an accelerator at all — this is the documented default on purpose (TrainConfig.accelerator's own docstring: "device is intentionally absent: PTL auto-detects accelerator via Trainer(accelerator="auto")"). xla_accelerator is now computed by a helper, _accelerator_resolves_to_xla, that also checks XLAAccelerator.is_available() for "auto" — it follows the same priority order (XLAAccelerator checked first) that lightning_fabric.utilities.device_parser._select_auto_accelerator uses.
  • num_nodes > 1 with a single device per host (a multi-host XLA topology). The guard's own multi-device check, _requests_multiple_devices(devices, accelerator), only looked at devices. The pre-existing distributed_requested computation a few lines below already treats num_nodes > 1 as distributed too, so now the guard checks the same condition.

Scope

  • Single-device, single-node XLA strategy selection stays the same — "auto" never reaches the DDP branch there, so it still resolves to SingleDeviceXLAStrategy. The precision-plugin branch (XLAPrecision vs. a raw precision= string) now also activates for accelerator="auto" resolving to XLA on a single device, because it reads the same xla_accelerator value as the strategy guard. This was already needed (XLAStrategy/SingleDeviceXLAStrategy reject a raw precision string) and gives the same resolved precision value either way.
  • An explicitly passed strategy is never overridden. An explicit "ddp" still resolves to the same DDPStrategy object as before — that strategy_name == "ddp" branch already existed, is unrelated to this change, and fires the same with or without XLA.
  • Non-XLA accelerators don't change: the guard requires xla_accelerator, and _accelerator_resolves_to_xla returns False for any accelerator other than "xla"/"tpu"/an XLA-resolving "auto".
  • Keypoint models are excluded from this fix. They train under manual optimization, and the find_unused_parameters=True handling that the DDP branch relies on has no validated XLA equivalent. So use_grouppose_keypoints=True configs still hit the pre-existing DDPStrategy/XLAAccelerator mismatch, instead of silently running a combination nobody validated.
  • Segmentation models are not excluded — the guard doesn't special-case segmentation_head, so a segmentation config reaches "xla" the same way plain detection does.

Validation

TestAcceleratorResolvesToXLA unit-tests _accelerator_resolves_to_xla directly (mocking XLAAccelerator.is_available, no torch_xla needed): explicit "tpu"/"xla" (case-insensitive) are always XLA; "auto" matches whatever is_available() reports; "cpu"/"gpu" are never XLA no matter what. 7/7 passed.

TestMultiDeviceXLAStrategy covers build_trainer's actual strategy dispatch. The first four cases need real torch_xla (marked xla, importorskip-guarded) because build_trainer builds a real XLAPrecision on this path. The newer cases patch XLAPrecision instead (and, for accelerator="auto", XLAAccelerator.is_available), the same way TestBuildTrainerPrecision's existing XLA tests already do, so they run on every CI lane:

Case Result
test_accelerator_auto_resolves_to_xla_strategy_when_xla_is_available (accelerator="auto", devices=4, XLA mocked available) PASSED — strategy == "xla"
test_multi_node_single_device_selects_xla_strategy (accelerator="tpu", devices=1, num_nodes=2) PASSED — strategy == "xla"
test_multi_device_xla_strategy_is_selected_for_segmentation_models (accelerator="tpu", devices=4, segmentation_head=True) PASSED — strategy == "xla"

Reverting only the source fix (the whole if (...): strategy = "xla" block disabled, tests kept) turns all three into failures — strategy stays a DDPStrategy object instead of "xla". So each test is checking the guard it targets, not passing by accident.

On a Cloud TPU v6e-1 (southamerica-west1-a, runtime v2-alpha-tpuv6e), torch 2.9.1, torch_xla 2.9.0, libtpu 0.0.21, pytorch-lightning 2.6.5, against this base commit, with accelerator="tpu" passed explicitly:

TestMultiDeviceXLAStrategy (pre-existing, real hardware) with the fix whole guard reverted, tests kept
test_multiple_xla_devices_select_the_xla_strategy PASSED FAILEDassert <DDPStrategy object...>
test_single_xla_device_keeps_auto PASSED PASSED
test_an_explicit_strategy_is_never_overridden PASSED PASSED
test_multi_device_xla_strategy_is_not_selected_for_keypoint_models with the fix only the not has_keypoints clause removed (guard otherwise intact)
same test PASSED — isinstance(strategy, DDPStrategy) FAILEDstrategy is the string "xla", not a DDPStrategy

That last row is a different mutation on purpose. Removing the entire guard leaves keypoint configs on the pre-existing DDP branch anyway, so that assertion would hold either way and wouldn't tell us anything about the keypoint exclusion specifically. Removing only the has_keypoints check isolates it instead. Both mutations ran locally against the CPU-mocked XLAPrecision path (_mc(use_grouppose_keypoints=True) etc.) with the same result, then got re-verified with real torch_xla on the arm shown above.

On a Cloud TPU v6e-4 (4 chips, xla:0-xla:3), same base commit, accelerator="tpu" and devices=4 passed explicitly (not the accelerator="auto" default — that path is only covered by the CPU-mocked test above, not yet by real hardware):

Arm Result
develop unpatched ValueError: The \XLAAccelerator` can only be used with a `SingleDeviceXLAStrategy` or `XLAStrategy`, found DDPStrategy.`
with this change builds; strategy=XLAStrategy, accelerator=XLAAccelerator, Lightning reports TPU available: True, using: 4 TPU cores

The unpatched run prints rf-detr's own line right before it fails, and that's where the DDPStrategy comes from:

[INFO] rf-detr - strategy='auto' with distributed execution → DDPStrategy(find_unused_parameters=True).

test_all_gather_multiprocess_xla_collective_routing also passes on that slice in 18.11 s, so the four chips are doing real multi-replica work.

Training end to end across replicas was measured separately on 2026-08-24, on an earlier develop commit, on a v5litepod-4, with strategy="xla" passed explicitly (the pre-existing workaround, not touched by this change — not the accelerator="auto" auto-detection path this PR adds): the same configuration went from failing in 6.3 s to completing 2 epochs over 468 images in 919.2 s. This PR makes no multi-chip throughput claim — on that small dataset four chips weren't faster than one, since each replica pays its own warm-up.

Still not verified on real TPU silicon: accelerator="auto" resolving to XLA, num_nodes > 1, and a segmentation config reaching the guard. All three are covered by the CPU-mocked tests above, but not by a real multi-chip/multi-host run.

Refs #1058.

@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86%. Comparing base (06852a6) to head (4b59943).

Additional details and impacted files
@@           Coverage Diff           @@
##           develop   #1427   +/-   ##
=======================================
  Coverage       86%     86%           
=======================================
  Files          114     114           
  Lines        15052   15062   +10     
=======================================
+ Hits         13006   13016   +10     
  Misses        2046    2046           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JESUSROYETH
JESUSROYETH force-pushed the fix/xla-multi-device-strategy branch from 079f17c to b3a9049 Compare September 4, 2026 14:35
@Borda
Borda requested a balanced review from Copilot September 4, 2026 20:56

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.

🟡 Changes recommended

The source comments, tests, and changelog incorrectly attribute RF-DETR’s DDP selection to Lightning.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Fixes multi-device and multi-node XLA trainer configuration by selecting a compatible strategy.

Changes:

  • Detects explicit and auto-resolved XLA accelerators.
  • Selects XLAStrategy for distributed non-keypoint training.
  • Adds strategy tests and release notes.
File summaries
File Description
src/rfdetr/training/trainer.py Adds XLA detection and strategy selection.
tests/training/test_build_trainer.py Tests XLA resolution and dispatch scenarios.
CHANGELOG.md Documents the training fix.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread CHANGELOG.md Outdated
Comment thread src/rfdetr/training/trainer.py Outdated
Comment thread tests/training/test_build_trainer.py Outdated
Changes:
- Restrict automatic XLA strategy promotion to multiple local devices and document that one-device-per-host XLA remains unvalidated.
- Correct the DDP root-cause documentation and extend auto-XLA coverage to assert its precision-plugin contract.

Impact:
- Prevents the trainer from claiming an unsupported one-device multi-node XLA topology while preserving supported multi-device XLA routing.
- Keeps release notes and regression tests aligned with RF-DETR's actual DDP control flow.

Verification:
- `pre-commit run --all-files` passed.
- Focused XLA trainer suite: 10 passed, 4 hardware-dependent skips.

Residual limits:
- No real TPU or multi-host XLA execution was run.
- GitHub review threads and portable review-artifact promotion require external authorized environments.

---

Co-authored-by: Codex <codex@openai.com>
@Borda
Borda merged commit b9b1a64 into roboflow:develop Sep 4, 2026
42 checks passed
Borda added a commit that referenced this pull request Sep 7, 2026
…1427)

* select XLAStrategy for multi-device XLA/TPU training
* restrict XLA promotion to local multi-device runs

---------

Co-authored-by: Jesús Royeth <JESUSROYETH@users.noreply.github.com>
Co-authored-by: Jirka Borovec <6035284+Borda@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
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.

3 participants