Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

223 lines
11 KiB
ReStructuredText

.. _libcudacxx-extended-api-memory-model:
Memory model
============
Standard C++ presents a view that the cost to synchronize threads is uniform and low.
CUDA C++ is different: the cost to synchronize threads grows as threads are further apart. It is low across threads
within a block, but high across arbitrary threads in the system running on multiple GPUs and CPUs.
To account for non-uniform thread synchronization costs that are not always low, CUDA C++ extends the standard C++
memory model and concurrency facilities in the ``cuda::`` namespace with **thread scopes**, retaining the syntax and
semantics of standard C++ by default.
.. _libcudacxx-extended-api-memory-model-thread-scopes:
Thread Scopes
-------------
A **thread scope** specifies the kind of threads that can synchronize with each other using a synchronization primitive such
as :ref:`atomic <libcudacxx-extended-api-synchronization-atomic>` or
:ref:`barrier <libcudacxx-extended-api-synchronization-barrier>`.
.. code:: cuda
namespace cuda {
enum thread_scope {
thread_scope_system,
thread_scope_device,
thread_scope_block,
thread_scope_thread
};
} // namespace cuda
Scope Relationships
~~~~~~~~~~~~~~~~~~~
Each program thread is related to each other program thread by one or more thread scope relations:
- Each thread in the system is related to each other thread in the system by the *system* thread scope:
``thread_scope_system``.
- Each GPU thread is related to each other GPU thread in the same CUDA device and within the same `memory
synchronization domain <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#memory-synchronization-domains>`__
by the *device* thread scope: ``thread_scope_device``.
- Each GPU thread is related to each other GPU thread in the same CUDA thread block by the *block* thread scope:
``thread_scope_block``.
- Each thread is related to itself by the ``thread`` thread scope: ``thread_scope_thread``.
Synchronization primitives
--------------------------
Types in namespaces ``std::`` and ``cuda::std::`` have the same behavior as corresponding types in namespace ``cuda::``
when instantiated with a scope of ``cuda::thread_scope_system``.
Atomicity
---------
An atomic operation is atomic at the scope it specifies if:
- it specifies a scope other than ``thread_scope_system``, **or**
- the scope is ``thread_scope_system`` and:
- it affects an object in `system allocated memory <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#um-unified-memory-programming-hd>`__ and `pageableMemoryAccess <https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__TYPES.html#group__CUDART__TYPES_1gg49e2f8c2c0bd6fe264f2fc970912e5cddc80992427a92713e699953a6d249d6f>`__ is ``1`` [0], **or**
- it affects an object in `managed
memory <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#um-unified-memory-programming-hd>`__
and
`concurrentManagedAccess <https://docs.nvidia.com/cuda/cuda-runtime-api/structcudaDeviceProp.html#structcudaDeviceProp_116f9619ccc85e93bc456b8c69c80e78b>`__
is ``1``, **or**
- it affects an object in `mapped
memory <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#mapped-memory>`__ and
`hostNativeAtomicSupported <https://docs.nvidia.com/cuda/cuda-runtime-api/structcudaDeviceProp.html#structcudaDeviceProp_1ef82fd7d1d0413c7d6f33287e5b6306f>`__
is ``1``, **or**
- it is a load or store that affects a naturally-aligned object of
sizes ``1``, ``2``, ``4``, ``8``, or ``16`` bytes on `mapped
memory <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#mapped-memory>`__ [1],
**or**
- it affects an object in GPU memory, only GPU threads access it, and
- `p2pNativeAtomicSupported <https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__TYPES.html#group__CUDART__TYPES_1gg2f597e2acceab33f60bd61c41fea0c1b8513982962e4439fa60f2a24348be587>`__ between each accessing GPU and the GPU where the object resides is ``1``, or
- only GPU threads from a single GPU concurrently access it.
.. note::
- [0] If `PageableMemoryAccessUsesHostPagetables <https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__TYPES.html#group__CUDART__TYPES_1gg49e2f8c2c0bd6fe264f2fc970912e5cdc228cf8983c97d0e035da72a71494eaa>`__ is ``0`` then atomic operations to memory mapped file or ``hugetlbfs`` allocations are not atomic.
- [1] If `hostNativeAtomicSupported <https://docs.nvidia.com/cuda/cuda-runtime-api/structcudaDeviceProp.html#structcudaDeviceProp_1ef82fd7d1d0413c7d6f33287e5b6306f>`__ is ``0``, atomic load or store operations at system scope that affect a
naturally-aligned 16-byte wide object in
`unified memory <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#um-unified-memory-programming-hd>`__ or
`mapped memory <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#mapped-memory>`__ require system
support. NVIDIA is not aware of any system that lacks this support and there is no CUDA API query available to
detect such systems.
Refer to the `CUDA programming guide <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html>`__
for more information on
`system allocated memory <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#um-unified-memory-programming-hd>`__,
`managed memory <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#um-unified-memory-programming-hd>`__,
`mapped memory <https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#mapped-memory>`__,
CPU memory, and GPU memory.
Data Races
----------
Modify `intro.races paragraph 21 <https://eel.is/c++draft/intro.races#21>`__ of ISO/IEC IS 14882 (the C++ Standard)
as follows:
The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at
least one of which is not atomic **at a scope that includes the thread that performed the other operation**, and neither
happens before the other, except for the special case for signal handlers described below.
Any such data race results in undefined behavior. […]
Modify `thread.barrier.class paragraph 4 <https://eel.is/c++draft/thread.barrier.class#4>`__ of ISO/IEC IS
14882 (the C++ Standard) as follows
4. Concurrent invocations of the member functions of ``barrier``, other than its destructor, do not introduce data
races **as if they were atomic operations**. […]
Modify `thread.latch.class paragraph 2 <https://eel.is/c++draft/thread.latch.class#2>`__ of ISO/IEC IS 14882
(the C++ Standard) as follows:
2. Concurrent invocations of the member functions of ``latch``, other than its destructor, do not introduce data
races **as if they were atomic operations**. […]
Modify `thread.sema.cnt paragraph 3 <https://eel.is/c++draft/thread.sema.cnt#3>`__ of ISO/IEC IS 14882
(the C++ Standard) as follows:
3. Concurrent invocations of the member functions of ``counting_semaphore``, other than its destructor, do not
introduce data races **as if they were atomic operations**.
Modify `thread.stoptoken.intro paragraph 5 <https://eel.is/c++draft/thread#stoptoken.intro-5>`__ of ISO/IEC IS
14882 (the C++ Standard) as follows:
Calls to the functions ``request_stop``, ``stop_requested``, and ``stop_possible`` do not introduce data
races **as if they were atomic operations**. […]
Modify `atomics.fences paragraph 2 through 4 <https://eel.is/c++draft/atomics.fences#2>`__ of ISO/IEC IS 14882 (the
C++ Standard) as follows:
A release fence A synchronizes with an acquire fence B if there exist atomic operations X and Y, both operating on
some atomic object M, such that A is sequenced before X, X modifies M, Y is sequenced before B, and Y reads the value
written by X or a value written by any side effect in the hypothetical release sequence X would head if it were a
release operation, **and each operation (A, B, X, and Y) specifies a scope that includes the thread that performed
each other operation**.
A release fence A synchronizes with an atomic operation B that performs an acquire operation on an atomic object M if
there exists an atomic operation X such that A is sequenced before X, X modifies M, and B reads the value written by
X or a value written by any side effect in the hypothetical release sequence X would head if it were a release
operation, **and each operation (A, B, and X) specifies a scope that includes the thread that performed each other
operation**.
An atomic operation A that is a release operation on an atomic object M synchronizes with an acquire fence B if
there exists some atomic operation X on M such that X is sequenced before B and reads the value written by A or a
value written by any side effect in the release sequence headed by A, **and each operation (A, B, and X) specifies
a scope that includes the thread that performed each other operation**.
.. _libcudacxx-extended-api-memory-model-message-passing:
Example: Message Passing
------------------------
The following example passes a message stored to the ``x`` variable by a
thread in block ``0`` to a thread in block ``1`` via the flag ``f``:
.. code:: cpp
int x = 0;
int f = 0;
.. list-table::
:widths: 50 50
:header-rows: 1
* - Thread 0 Block 0
- Thread 0 Block 1
* -
.. code:: cpp
x = 42;
cuda::atomic_ref<int, cuda::thread_scope_device> flag(f);
flag.store(1, memory_order_release);
-
.. code:: cpp
cuda::atomic_ref<int, cuda::thread_scope_device> flag(f);
while(flag.load(memory_order_acquire) != 1);
assert(x == 42);
In the following variation of the previous example, two threads
concurrently access the ``f`` object without synchronization, which
leads to a **data race**, and exhibits **undefined behavior**:
.. code:: cpp
int x = 0;
int f = 0;
.. list-table::
:widths: 50 50
:header-rows: 1
* - Thread 0 Block 0
- Thread 0 Block 1
* -
.. code:: cpp
x = 42;
cuda::atomic_ref<int, cuda::thread_scope_block> flag(f);
flag.store(1, memory_order_release); // UB: data race
-
.. code:: cpp
cuda::atomic_ref<int, cuda::thread_scope_device> flag(f);
while(flag.load(memory_order_acquire) != 1); // UB: data race
assert(x == 42);
While the memory operations on ``f`` - the store and the loads - are
atomic, the scope of the store operation is “block scope”. Since the
store is performed by Thread 0 of Block 0, it only includes all other
threads of Block 0. However, the thread doing the loads is in Block 1,
i.e., it is not in a scope included by the store operation performed in
Block 0, causing the store and the load to not be “atomic”, and
introducing a data-race.
For more examples see the `PTX memory consistency model litmus
tests <https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#axioms>`__.