Skip to content

arch: Validate device IDs and correct device initialization - #3067

Draft
ggorman wants to merge 3 commits into
fix-default-device-idfrom
fix-device-id-validation
Draft

ggorman wants to merge 3 commits into
fix-default-device-idfrom
fix-device-id-validation

Conversation

@ggorman

@ggorman ggorman commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Stacked on #3066; the base switches to main once that merges.

  1. Raise on a device ID that does not exist. An explicit deviceid at or beyond the number of devices (the visible ones if e.g. CUDA_VISIBLE_DEVICES is set) was ignored by the generated code, which ran on the current device; the memory query raised IndexError (an off-by-one on the visible devices) or wrapped around them. DeviceID._arg_check now raises InvalidArgument: Device 2 does not exist: 2 device(s) available (CUDA_VISIBLE_DEVICES=0,1) before anything is launched. Tests: TestDeviceID::test_nonexistent_deviceid[None|0,1]; test_deviceid_per_rank now picks devices that exist on any node.
  2. Select the device before initializing it, and only if there is one. The OpenACC runtime was initialized before selecting the device, so every MPI rank not on the default device also created a context on it (seen with nvidia-smi on two H200s: odd ranks held contexts on both GPUs; after the fix one each). With no device found, the MPI default selection computed rank % 0, a SIGFPE with an OpenMP runtime reporting zero devices. Tests: test_gpu_openacc.py::TestCodeGeneration::test_init_acc_env; test_gpu_openmp.py::test_init_omp_env_w_mpi expectation updated.

Each test fails on the parent commit.

Coordination with #2930: this PR validates the device ID in arguments(), so with both changes #2930's test_no_visible_devices raises inside op.arguments() (verified for the masks -1, '' and NoDevFiles); that call needs to move inside pytest.raises. Whichever lands second rebases.

Refs #2711, #2930. Out of scope, as follow-ups: visible-device lists naming devices the process cannot see (e.g. a container with --gpus device=1 and CUDA_VISIBLE_DEVICES=1), UUID/MIG masks, and validating deviceid in estimate_memory(). Does not close #2711.

🤖 Generated with Claude Code

With no visible-device environment variable, the generated code selects
device rank % ngpus, but ArgumentsMap._physical_deviceid used the bare
rank. With more ranks than GPUs (e.g., two decoupler workers on a 1-GPU
node), the device memory query then fails (None) and nbytes_avail_mapper
raises TypeError: unsupported operand type(s) for -=: 'NoneType' and 'Integer'.
An explicit `deviceid` at or beyond the number of devices (visible ones, if
e.g. CUDA_VISIBLE_DEVICES is set) was silently ignored by the generated
code, which then ran on the current device, while the memory query either
raised IndexError (an off-by-one on the visible devices) or wrapped around
them. Check it along with the other runtime arguments and raise an
InvalidArgument naming the device and the number available
…is one

The OpenACC runtime was initialized before selecting the device, so every
rank not using the default device also created a context on it. And with no
device found, the MPI device selection computed `rank % 0`
deviceid = self.get('deviceid', -1)
var, visible_devices = get_visible_devices()
if visible_devices is None:
ngpus = self.platform.numa_domains

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.

That doesn't seem right, if there is no visible device we can't just create ngpus device ids none of those ids will be valid.

Comment thread tests/test_gpu_common.py
Comment on lines +209 to +213
# Reversed order to ensure it is different to default
rank = grid.distributor.myrank
ngpus = op._platform.numa_domains
deviceid = (ngpus - 1 - rank) % ngpus

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.

This change seems seems pointless, possibly even detrimental when there is only one GPU on the system?

if not isinstance(self.platform, Device):
return None

# The logical device ID is relative to the visible devices, if any

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.

This seems like a lot of unnecessary churn for what is ultimately a small tweak.

raise InvalidArgument(
f"Device {deviceid} does not exist: {ngpus} device(s) available"
f"{visible}"
)

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.

Previous behaviour was just to wrap around and warn the user. This seems like a downgrade in behaviour

Comment on lines +1458 to +1459
if visible_devices is None:
return deviceid

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.

This was checked earlier, why is it being checked again here?

@ggorman
ggorman force-pushed the fix-default-device-id branch from cddcdcb to f8d6cbb Compare October 21, 2026 21:53

This branch has not been deployed

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

Labels

arch jitting, archinfo, ... GPU

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants