diff --git a/cuda_core/cuda/core/_device_resources.pyi b/cuda_core/cuda/core/_device_resources.pyi index 1544d71b28..8ae6fb6b32 100644 --- a/cuda_core/cuda/core/_device_resources.pyi +++ b/cuda_core/cuda/core/_device_resources.pyi @@ -47,8 +47,19 @@ class WorkqueueResourceOptions: sharing_scope : str, optional Workqueue sharing scope. Accepted values: ``"device_ctx"`` or ``"green_ctx_balanced"``. (Default to ``None``) + concurrency_limit : int, optional + Expected maximum number of concurrent stream-ordered + workloads. Must be ``>= 1`` when set. The effective + driver-side cap is ``CUDA_DEVICE_MAX_CONNECTIONS`` + (typically ``[1, 32]``); configurations may exceed + this cap, but the driver will not guarantee that work + submission remains non-overlapping. (Default to ``None``) """ sharing_scope: str | None = None + concurrency_limit: int | None = None + + def __post_init__(self): + ... class SMResource: """Represent an SM (streaming multiprocessor) resource partition. @@ -120,7 +131,7 @@ class WorkqueueResource: Parameters ---------- options : :obj:`WorkqueueResourceOptions` - Configuration options (sharing scope, etc.). + Configuration options (sharing scope, concurrency limit). """ class DeviceResources: diff --git a/cuda_core/cuda/core/_device_resources.pyx b/cuda_core/cuda/core/_device_resources.pyx index bafc462c93..1e1f016f6c 100644 --- a/cuda_core/cuda/core/_device_resources.pyx +++ b/cuda_core/cuda/core/_device_resources.pyx @@ -129,9 +129,28 @@ cdef class WorkqueueResourceOptions: sharing_scope : str, optional Workqueue sharing scope. Accepted values: ``"device_ctx"`` or ``"green_ctx_balanced"``. (Default to ``None``) + concurrency_limit : int, optional + Expected maximum number of concurrent stream-ordered + workloads. Must be ``>= 1`` when set. The effective + driver-side cap is ``CUDA_DEVICE_MAX_CONNECTIONS`` + (typically ``[1, 32]``); configurations may exceed + this cap, but the driver will not guarantee that work + submission remains non-overlapping. (Default to ``None``) """ sharing_scope: str | None = None + concurrency_limit: int | None = None + + def __post_init__(self): + if self.sharing_scope not in (None, "device_ctx", "green_ctx_balanced"): + raise ValueError( + f"Unknown sharing_scope: {self.sharing_scope!r}. " + "Expected 'device_ctx' or 'green_ctx_balanced'." + ) + if self.concurrency_limit is not None and self.concurrency_limit < 1: + raise ValueError( + f"concurrency_limit must be >= 1, got {self.concurrency_limit}" + ) cdef inline int _validate_split_field_length( @@ -541,17 +560,21 @@ cdef class WorkqueueResource: Parameters ---------- options : :obj:`WorkqueueResourceOptions` - Configuration options (sharing scope, etc.). + Configuration options (sharing scope, concurrency limit). """ cdef WorkqueueResourceOptions opts = check_or_create_options( WorkqueueResourceOptions, options, "Workqueue resource options" ) _check_green_ctx_support() _check_workqueue_support() - if opts.sharing_scope is None: + if opts.sharing_scope is None and opts.concurrency_limit is None: return None IF CUDA_CORE_BUILD_MAJOR >= 13: + if opts.concurrency_limit is not None: + self._wq_config_resource.wqConfig.wqConcurrencyLimit = ( + opts.concurrency_limit + ) if opts.sharing_scope == "device_ctx": self._wq_config_resource.wqConfig.sharingScope = ( cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_DEVICE_CTX @@ -560,11 +583,6 @@ cdef class WorkqueueResource: self._wq_config_resource.wqConfig.sharingScope = ( cydriver.CUdevWorkqueueConfigScope.CU_WORKQUEUE_SCOPE_GREEN_CTX_BALANCED ) - else: - raise ValueError( - f"Unknown sharing_scope: {opts.sharing_scope!r}. " - "Expected 'device_ctx' or 'green_ctx_balanced'." - ) ELSE: raise RuntimeError( "WorkqueueResource requires cuda.core to be built with CUDA 13.x bindings" diff --git a/cuda_core/docs/source/release/1.1.0-notes.rst b/cuda_core/docs/source/release/1.1.0-notes.rst index 529c9df9eb..d9fe0a7a5b 100644 --- a/cuda_core/docs/source/release/1.1.0-notes.rst +++ b/cuda_core/docs/source/release/1.1.0-notes.rst @@ -106,6 +106,10 @@ New features (`#2068 `__, closes `#2049 `__) +- Added ``concurrency_limit`` to :class:`WorkqueueResourceOptions` to + configure the expected maximum concurrent stream-ordered workloads. + (`#2329 `__) + Fixes and enhancements ---------------------- diff --git a/cuda_core/tests/test_green_context.py b/cuda_core/tests/test_green_context.py index 4078b166b7..ee3896c53e 100644 --- a/cuda_core/tests/test_green_context.py +++ b/cuda_core/tests/test_green_context.py @@ -216,9 +216,28 @@ def test_configure_none_is_noop(self, wq_resource): def test_configure_valid_scope(self, wq_resource): wq_resource.configure(WorkqueueResourceOptions(sharing_scope="green_ctx_balanced")) - def test_configure_invalid_scope_raises(self, wq_resource): + def test_invalid_scope_raises_at_construction(self): with pytest.raises(ValueError, match="Unknown sharing_scope"): - wq_resource.configure(WorkqueueResourceOptions(sharing_scope="bogus")) + WorkqueueResourceOptions(sharing_scope="bogus") + + def test_configure_concurrency_limit(self, wq_resource): + wq_resource.configure(WorkqueueResourceOptions(concurrency_limit=4)) + + def test_configure_concurrency_and_scope(self, wq_resource): + wq_resource.configure( + WorkqueueResourceOptions( + sharing_scope="green_ctx_balanced", + concurrency_limit=2, + ) + ) + + def test_concurrency_limit_zero_raises_at_construction(self): + with pytest.raises(ValueError, match="concurrency_limit must be >= 1"): + WorkqueueResourceOptions(concurrency_limit=0) + + def test_concurrency_limit_negative_raises_at_construction(self): + with pytest.raises(ValueError, match="concurrency_limit must be >= 1"): + WorkqueueResourceOptions(concurrency_limit=-3) # --------------------------------------------------------------------------- @@ -495,9 +514,15 @@ def test_two_green_contexts_independent(self, init_cuda, sm_resource, fill_kerne ctx_a.close() def test_with_workqueue_resource(self, init_cuda, sm_resource, wq_resource, fill_kernel): - """Green context with SM + workqueue resources can launch a kernel.""" + """Green context with SM + configured workqueue can launch a kernel.""" dev = init_cuda groups, _ = sm_resource.split(SMResourceOptions(count=None)) + wq_resource.configure( + WorkqueueResourceOptions( + sharing_scope="green_ctx_balanced", + concurrency_limit=4, + ) + ) try: ctx = dev.create_context(ContextOptions(resources=[groups[0], wq_resource]))