sycl::atomic_ref
#
template <typename T, sycl::memory_order DefaultOrder, sycl::memory_scope DefaultScope,
sycl::access::address_space AddressSpace = sycl::access::address_space::generic_space>
class atomic_ref;
The sycl::atomic_ref
class provides the ability to perform
atomic operations in device code with a syntax similar to the
C++ standard std::atomic_ref
. The sycl::atomic_ref
class must not be used in host code.
The atomic operations and member functions behave as described in the C++ specification, barring the restrictions discussed below.
Note
Care must be taken when using atomics for work-item coordination, because work-items are not required to provide stronger than weakly parallel forward progress guarantees. Operations that block a work-item, such as continuously checking the value of an atomic variable until some condition holds, or using atomic operations that are not lock-free, may prevent overall progress.
See also
SYCL Specification Section 4.15.3
Template parameters#
DefaultOrder
#
Unlike std::atomic_ref
, sycl::atomic_ref
does not provide
a default memory ordering for its operations. Instead, the
application must specify a default ordering via the DefaultOrder
template parameter. This ordering is used as a default for most of
the atomic operations, but most member functions also provide an
optional parameter that allows the application to override this
default.
The set of supported orderings is specific to a device, but every device is guaranteed to support at least
sycl::memory_order::relaxed
.If the default order is set to
sycl::memory_order::relaxed
, all memory order arguments default tosycl::memory_order::relaxed
.If the default order is set to
sycl::memory_order::acq_rel
, memory order arguments default tosycl::memory_order::acquire
for load operations,sycl::memory_order::release
for store operations andsycl::memory_order::acq_rel
for read-modify-write operations.If the default order is set to
sycl::memory_order::seq_cst
, all memory order arguments default tosycl::memory_order::seq_cst
.
DefaultScope
#
The sycl::atomic_ref
class has a template parameter DefaultScope
,
which allows the application to define a default memory scope for the
atomic operations. Most member functions also provide an optional parameter
that allows the application to override this default.
AddressSpace
#
The sycl::atomic_ref
class has a template parameter AddressSpace
,
which allows the application to make an assertion about the address space
of the object of type T
that it references.
The default value for this
parameter is sycl::access::address_space::generic_space
, which indicates
that the object could be in either the global or local address spaces.
If the application knows the address space, it can set this template
parameter to either sycl::access::address_space::global_space
or
sycl::access::address_space::local_space
as an assertion to
the implementation.
Specifying the address space via this template parameter may allow the implementation to perform certain optimizations. Specifying an address space that does not match the object's actual address space results in undefined behavior.
Type T
#
The template parameter T must be one of the following types:
int
unsigned int
long
unsigned long
long long
unsigned long long
float
double
In addition, the type T
must satisfy one of the
following conditions:
sizeof(T) == 4
sizeof(T) == 8
and the code containing thissycl::atomic_ref
was submitted to a device that hassycl::aspect::atomic64
.
For floating-point types, the member functions of the sycl::atomic_ref
class may be emulated, and they may use a different floating-point
environment from those defined by sycl::info::device::single_fp_config
and sycl::info::device::double_fp_config
(i.e. floating-point atomics
may use different rounding modes and may have different exception behavior).
Static members#
The static member
required_alignment
describes the minimum required alignment in bytes of an object that can be referenced by ansycl::atomic_ref<T>
, which must be at leastalignof(T)
.The static member
is_always_lock_free
istrue
if all atomic operations for typeT
are always lock-free. A SYCL implementation is not guaranteed to support atomic operations that are not lock-free.The static members
default_read_order
,default_write_order
anddefault_read_modify_write_order
reflect the default memory order values for each type of atomic operation, consistent with theDefaultOrder
template.
(constructors)#
atomic_ref(T& ref);
Constructs an instance of sycl::atomic_ref
which is
associated with the reference ref
.
Member functions#
Available on any sycl::atomic_ref<T>
object#
is_lock_free
#
bool is_lock_free() const;
Return true
if the atomic operations provided
by this sycl::atomic_ref
are lock-free.
store
#
void store(T operand,
sycl::memory_order order = default_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically stores operand
to the object referenced by
this sycl::atomic_ref
. The memory order of this atomic
operation must be sycl::memory_order::relaxed
,
sycl::memory_order::release
or sycl::memory_order::seq_cst
.
This function is only supported for 64-bit data types on devices
that have sycl::aspect::atomic64
.
operator=
#
T operator=(T desired) const;
Equivalent to store(desired)
. Returns desired
.
load
#
T load(sycl::memory_order order = default_read_order,
sycl::memory_scope scope = default_scope) const
Atomically loads the value of the object referenced by this
sycl::atomic_ref
. The memory order of this atomic
operation must be sycl::memory_order::relaxed
,
sycl::memory_order::acquire
, or sycl::memory_order::seq_cst
.
This function is only supported for 64-bit data types on devices
that have sycl::aspect::atomic64
.
Cast operator to T
#
operator T() const;
Equivalent to load()
.
exchange
#
T exchange(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const
Atomically replaces the value of the object referenced by this
sycl::atomic_ref
with value operand
and returns the
original value of the referenced object. This function is only
supported for 64-bit data types on devices that have
sycl::aspect::atomic64
.
compare_exchange_weak
#
Overload 1
bool compare_exchange_weak(T& expected, T desired,
sycl::memory_order success,
sycl::memory_order failure,
sycl::memory_scope scope = default_scope) const;
Atomically compares the value of the object referenced by
this sycl::atomic_ref
against the value of expected
.
If the values are equal, attempts to replace the value of the
referenced object with the value of desired
; otherwise assigns
the original value of the referenced object to expected
.
Returns true
if the comparison operation and replacement
operation were successful. The failure
memory order of
this atomic operation must be sycl::memory_order::relaxed
,
sycl::memory_order::acquire
or sycl::memory_order::seq_cst
.
This function is only supported for 64-bit data types on devices
that have sycl::aspect::atomic64
.
Overload 2
bool compare_exchange_weak(T& expected, T desired,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Equivalent to
compare_exchange_weak(expected, desired, order, order, scope)
.
compare_exchange_strong
#
Overload 1
bool compare_exchange_strong(T& expected, T desired,
sycl::memory_order success,
sycl::memory_order failure,
sycl::memory_scope scope = default_scope) const;
Atomically compares the value of the object referenced by
this sycl::atomic_ref
against the value of expected.
If the values are equal, replaces the value of the referenced
object with the value of desired
; otherwise assigns the
original value of the referenced object to expected
.
Returns true
if the comparison operation and replacement
operation were successful. The failure
memory order of
this atomic operation must be sycl::memory_order::relaxed
,
sycl::memory_order::acquire
or sycl::memory_order::seq_cst
.
This function is only supported for 64-bit data types on devices
that have sycl::aspect::atomic64
.
Overload 2
bool compare_exchange_strong(T& expected, T desired,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Equivalent to
compare_exchange_strong(expected, desired, order, order, scope)
.
Available on sycl::atomic_ref<T>
object for integral T
#
fetch_add
#
T fetch_add(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically adds operand
to the value of the object
referenced by this sycl::atomic_ref
and assigns the
result to the value of the referenced object. Returns
the original value of the referenced object. This function
is only supported for 64-bit data types on devices that
have sycl::aspect::atomic64
.
operator+=
#
T operator+=(ptrdiff_t operand) const;
Equivalent to fetch_add(operand) + operand
.
operator++
#
T operator++(int) const;
Equivalent to fetch_add(1)
.
T operator++() const;
Equivalent to fetch_add(1) + 1
.
fetch_sub
#
T fetch_sub(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically subtracts operand
from the value of the
object referenced by this sycl::atomic_ref
and
assigns the result to the value of the referenced
object. Returns the original value of the referenced
object. This function is only supported for 64-bit
pointers on devices that have sycl::aspect::atomic64
.
operator-=
#
T operator-=(ptrdiff_t operand) const;
Equivalent to fetch_sub(operand) - operand
.
operator--
#
T operator--(int) const;
Equivalent to fetch_sub(1)
.
T operator--() const;
Equivalent to fetch_sub(1) - 1
.
fetch_and
#
T fetch_or(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const
Atomically performs a bitwise AND between operand
and the
value of the object referenced by this sycl::atomic_ref
,
and assigns the result to the value of the referenced object.
Returns the original value of the referenced object.
This function is only supported for 64-bit data types on
devices that have sycl::aspect::atomic64
.
operator&=
#
T operator&=(T operand) const;
Equivalent to fetch_and(operand) & operand
.
fetch_or
#
T fetch_or(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const
Atomically performs a bitwise OR between operand
and the
value of the object referenced by this sycl::atomic_ref
,
and assigns the result to the value of the referenced object.
Returns the original value of the referenced object.
This function is only supported for 64-bit data types on
devices that have sycl::aspect::atomic64
.
operator|=
#
T operator|=(T operand) const;
Equivalent to fetch_or(operand) | operand
.
fetch_xor
#
T fetch_xor(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const
Atomically performs a bitwise XOR between operand
and the
value of the object referenced by this sycl::atomic_ref
,
and assigns the result to the value of the referenced object.
Returns the original value of the referenced object.
This function is only supported for 64-bit data types on
devices that have sycl::aspect::atomic64
.
operator^=
#
T operator^=(T operand) const;
Equivalent to fetch_xor(operand) ^ operand
.
fetch_min
#
T fetch_min(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically computes the minimum of operand
and the value
of the object referenced by this sycl::atomic_ref
, and
assigns the result to the value of the referenced object.
Returns the original value of the referenced object. This
function is only supported for 64-bit data types on devices
that have sycl::aspect::atomic64
.
fetch_max
#
T fetch_max(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically computes the maximum of operand
and the value
of the object referenced by this sycl::atomic_ref
, and
assigns the result to the value of the referenced object.
Returns the original value of the referenced object. This
function is only supported for 64-bit data types on devices
that have sycl::aspect::atomic64
.
Available on sycl::atomic_ref<T>
object for floating-point T
#
fetch_add
#
T fetch_add(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically adds operand
to the value of the object referenced
by this sycl::atomic_ref
and assigns the result to the value
of the referenced object. Returns the original value of the
referenced object. This function is only supported for 64-bit data
types on devices that have sycl::aspect::atomic64
.
operator+=
#
T operator+=(T operand) const;
Equivalent to fetch_add(operand) + operand
.
fetch_sub
#
T fetch_sub(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const
Atomically subtracts operand
from the value of the object
referenced by this sycl::atomic_ref
and assigns the result to
the value of the referenced object. Returns the original value of
the referenced object. This function is only supported for 64-bit
data types on devices that have sycl::aspect::atomic64
.
operator-=
#
T operator-=(T operand) const;
Equivalent to fetch_sub(operand) - operand
.
fetch_min
#
T fetch_min(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically computes the minimum of operand
and the value
of the object referenced by this sycl::atomic_ref
, and
assigns the result to the value of the referenced object.
Returns the original value of the referenced object. This
function is only supported for 64-bit data types on devices
that have sycl::aspect::atomic64
.
fetch_max
#
T fetch_max(T operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically computes the maximum of operand
and the value
of the object referenced by this sycl::atomic_ref
, and
assigns the result to the value of the referenced object.
Returns the original value of the referenced object. This
function is only supported for 64-bit data types on devices
that have sycl::aspect::atomic64
.
Available on sycl::atomic_ref<T*>
object#
fetch_add
#
T* fetch_add(ptrdiff_t operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically adds operand
to the value of the object
referenced by this sycl::atomic_ref
and assigns the
result to the value of the referenced object. Returns
the original value of the referenced object. This function
is only supported for 64-bit pointers on devices that
have sycl::aspect::atomic64
.
operator+=
#
T* operator+=(ptrdiff_t operand) const;
Equivalent to fetch_add(operand) + operand
.
operator++
#
T* operator++(int) const;
Equivalent to fetch_add(1)
.
T* operator++() const;
Equivalent to fetch_add(1) + 1
.
fetch_sub
#
T* fetch_sub(ptrdiff_t operand,
sycl::memory_order order = default_read_modify_write_order,
sycl::memory_scope scope = default_scope) const;
Atomically subtracts operand
from the value of the
object referenced by this sycl::atomic_ref
and
assigns the result to the value of the referenced
object. Returns the original value of the referenced
object. This function is only supported for 64-bit
pointers on devices that have sycl::aspect::atomic64
.
operator-=
#
T* operator-=(ptrdiff_t operand) const;
Equivalent to fetch_sub(operand) - operand
.
operator--
#
T* operator--(int) const;
Equivalent to fetch_sub(1)
.
T* operator--() const;
Equivalent to fetch_sub(1) - 1
.
Examples#
1#include <sycl/sycl.hpp>
2
3#include <iomanip>
4#include <iostream>
5
6const int lsize = 10;
7
8// Allows concurrent push or concurrent pop, but not both
9struct List {
10 // Points to free spot on list
11 int *top;
12 int *data;
13
14 sycl::atomic_ref<int *, sycl::memory_order::relaxed,
15 sycl::memory_scope::work_group>
16 atomic_top;
17
18 List(sycl::queue q, int size) : atomic_top(top) {
19 data = sycl::malloc_shared<int>(size, q);
20 top = data;
21 };
22
23 bool empty() { return top == data; };
24
25 void push(int val) {
26 // Allocate space by moving top pointer to right
27 int *old_top = atomic_top.fetch_add(1);
28 *old_top = val;
29 };
30
31 auto pop() {
32 // Deallocate space by moving top pointer to left
33 int *old_top = atomic_top.fetch_add(-1);
34 return *(old_top - 1);
35 };
36};
37
38int main() {
39 sycl::queue q;
40
41 auto in = new (sycl::malloc_shared<List>(1, q)) List(q, lsize);
42 auto out = new (sycl::malloc_shared<List>(1, q)) List(q, lsize);
43
44 q.parallel_for(lsize, [=](auto idx) { in->push(idx); }).wait();
45
46 q.parallel_for(lsize, [=](auto idx) {
47 out->push(idx * 100 + in->pop());
48 }).wait();
49
50 std::cout << "Out:\n" << std::setfill('0');
51 while (!out->empty())
52 std::cout << std::setw(3) << out->pop() << "\n";
53}
Out:
900
801
702
603
504
405
306
207
108
009