cuda.core: introduce copy options for Buffer.copy_{to/from} - #2636
Conversation
|
/ok to test d835ecf |
This comment has been minimized.
This comment has been minimized.
9bb6d5e to
5080b34
Compare
|
/ok to test 5080b34 |
| is accepted); ``copy_batch`` always rejects graph capture, while | ||
| ``Buffer.copy_to``/``copy_from`` reject it only when ``options`` is given. |
There was a problem hiding this comment.
If a future version of CUDA adds graph support, this will need to be relaxed. Is the layered check for graph capture only duplicating what the driver already checks, or does it have a separate benefit? If we're trading extra logic and correctness risk only for a nicer error message it might not be worth it.
There was a problem hiding this comment.
Yes, if support for this becomes available the check for capturing streams would be dropped.
And you are right, the explicit check is to reject a capturing stream with a TypeError instead of a CUDAError, consistent with copy_batch.
| // cuMemcpyWithAttributesAsync (13.2+ — may be null on older drivers/bindings) | ||
| #if CUDA_VERSION >= 13020 | ||
| extern decltype(&cuMemcpyWithAttributesAsync) p_cuMemcpyWithAttributesAsync; | ||
| #else | ||
| // cuMemcpyWithAttributesAsync doesn't exist in CUDA < 13.2 headers, so use a | ||
| // void* placeholder. The pointer is always null when built against older CUDA. | ||
| extern void* p_cuMemcpyWithAttributesAsync; | ||
| #endif |
There was a problem hiding this comment.
I have concerns about (1) minor-version-specific version gating, and (2) scope-creep inherent in using resource_handles for things besides resource lifetime management. I see this merely extends an existing pattern, so neither of these should block merge; but I'd like to follow up.
…er shim Avoids a direct Cython cimport of cydriver.cuMemcpyWithAttributesAsync, which is absent from cuda-bindings < 13.2 and would fail to build (or fail to import for a mismatched install) whenever cuda-bindings 13.0/13.1 is paired with a CUDA-13 build. Mirrors the existing sm_resource_split (13.1+) shim pattern.
090444e to
9e7b50a
Compare
|
Summary
Adds an optional
optionskeyword argument toBuffer.copy_toandBuffer.copy_from, exposing the sameCopyOptionsdataclass introduced bycopy_batch(#2593) on the per-buffer path. Also fixes two issues incopy_batchitself, uncovered while aligning the two APIs: it now acceptsPER_THREAD_DEFAULT_STREAM(previously rejected, even though the driver accepts it), and it now raisesRuntimeErrorforsrc_access_order=DURING_API_CALLinstead of taking the pre-CUDA-13 fallback path because the fallback cannot emulate the synchronization behavior thatcudaMemcpyBatchedAsyncguarantees. Closes #2365.Public API
options=None(the default) selects the existingcuMemcpyAsyncpath with no change in behaviour.Behaviour
optionsis set,cuMemcpyWithAttributesAsyncis used only whencuda.bindingsand the driver are both CUDA 13.2 or newer, the stream is not currently capturing, and the stream is notLEGACY_DEFAULT_STREAM.optionstogether withLEGACY_DEFAULT_STREAMor a stream currently in graph capture mode raisesTypeError, matchingcopy_batch. These checks only apply whenoptionsis given;options=Nonenever triggers them, so existing code using either a default-stream token or graph capture is unaffected.cuda.bindings/driver install,options.src_access_ordervalues ofSTREAMandANYsilently fall back to plaincuMemcpyAsync(that fallback already provides stream-ordered access, which is what either value promises).DURING_API_CALLpromises all source reads complete before the call returns; a stream-ordered fallback cannot honor that, so it raisesRuntimeError.copy_batch's own pre-CUDA-13 fallback loop had the same hazard and is fixed the same way in this PR (see "Relation tocopy_batch" below).options=Nonenever raises for stream/capture reasons and always usescuMemcpyAsync, so existing code is unaffected.dst=Noneauto-allocation remains valid with options.Relation to
copy_batchUses the same
CopyOptionsdataclass, the shared_reject_unsupported_during_api_callhelper (_copy_enums.py) that guards the one hazardous fallback case (DURING_API_CALL), and the_to_cu_memcpy_attributesconversion helper (in the shared_copy_attributesmodule extracted in the prerequisite refactor PR).While aligning the two APIs, this PR also fixes
copy_batchitself:PER_THREAD_DEFAULT_STREAMunconditionally, even thoughcuMemcpyBatchAsyncaccepts it just likeBuffer.copy_to/copy_fromnow do; it now accepts it.CopyOptions, includingDURING_API_CALL, despite a comment claiming otherwise. It now raisesRuntimeErrorforDURING_API_CALLon that path, via the same shared_reject_unsupported_during_api_callhelper.The single-copy path with options now matches
copy_batch's rejection and fallback behavior in every respect but one:copy_batchBuffer.copy_to/copy_fromcuda.bindingsDURING_API_CALL→RuntimeError)DURING_API_CALL→RuntimeError)TypeErrorTypeError(only whenoptionsis given)LEGACY_DEFAULT_STREAMTypeErrorTypeError(only whenoptionsis given)PER_THREAD_DEFAULT_STREAMThe remaining difference is intentional:
copy_batchis a brand-new function with no backward-compatibility constraint, so it rejectsLEGACY_DEFAULT_STREAM/graph capture unconditionally.Buffer.copy_to/copy_fromare existing APIs;options=Nonemust not change behavior for existing callers, so it preserves the pre-#2365 behavior of accepting any default-stream token and remaining capturable into a graph.Implementation notes
_buffer.pyxcimports the new_with_attributes_availableCUDA 13.2 gate from that_copy_attributes;_copy_ops.pyxuses its own separate_batch_entry_point_availablegate (CUDA 13.0), sincecopy_batchand the per-buffer path have different minimum-version requirements.cydriver.cuMemcpyWithAttributesAsyncis absent fromcuda.bindingsolder than 13.2, so_buffer.pyxcannot cimport it directly without breaking builds against oldercuda.bindings. It is instead routed through a small C++ function-pointer shim (memcpy_with_attributes_asyncin_cpp/resource_handles.{cpp,hpp}), resolved at runtime, mirroring the existingsm_resource_split(13.1+) shim pattern.Tests
tests/memory/test_copy_single_options.pycovers:copy_toandcopy_fromwithoptions=Noneand each of the threeMemcpySrcAccessOrdervalues.overlap_mode=PREFER_OVERLAP_WITH_COMPUTEdoes not corrupt data.DURING_API_CALLis honored on the native path, or raisesRuntimeErroron the fallback path, depending on the installed driver/cuda.bindings.LEGACY_DEFAULT_STREAMwith options raisesTypeError;PER_THREAD_DEFAULT_STREAMwith options is accepted and honored.options=Nonecontinues to work under graph capture and withLEGACY_DEFAULT_STREAM, unaffected by the new checks.optionswith a capturing stream raisesTypeError(both methods).CopyOptionsvalues passed asoptions.src_location_hint,dst_location_hint).Host(numa_id=...),Host.numa_current()), skipped if the system reports no NUMA topology.dst=Noneauto-allocation with options.tests/memory/test_copy_batch.pyandtest_copy_batch_options.pycover thecopy_batchfixes:PER_THREAD_DEFAULT_STREAMis accepted and options are honored on it;LEGACY_DEFAULT_STREAMis still rejected.DURING_API_CALLraisesRuntimeErroron the fallback path, matching the per-buffer behavior._reject_unsupported_during_api_callhelper.tests/test_memory.pyadds coverage forBuffer.copy_to/copy_fromrejecting mismatched source/destination buffer sizes (a pre-existing check that had no test coverage).