Data#
- group data
Defines
-
LEGATE_TRUE_WHEN_DEBUG#
Typedefs
-
template<typename VAL, std::int32_t DIM = 1>
using Buffer = Legion::DeferredBuffer<VAL, DIM># A typed buffer class for intra-task temporary allocations.
Values in a buffer can be accessed by index expressions with Point objects, or via a raw pointer to the underlying allocation, which can be queried with the
Buffer::ptr()
method.Buffer is an alias to Legion::DeferredBuffer.
Note on using temporary buffers in CUDA tasks:
We use
Legion::DeferredBuffer
, whose lifetime is not connected with the CUDA stream(s) used to launch kernels. The buffer is allocated immediately at the point when create_buffer() is called, whereas the kernel that uses it is placed on a stream, and may run at a later point. Normally aLegion::DeferredBuffer
is deallocated automatically by Legion once all the kernels launched in the task are complete. However, aLegion::DeferredBuffer
can also be deallocated immediately usingLegion::DeferredBuffer::destroy()
, which is useful for operations that want to deallocate intermediate memory as soon as possible. This deallocation is not synchronized with the task stream, i.e. it may happen before a kernel which uses the buffer has actually completed. This is safe as long as we use the same stream on all GPU tasks running on the same device (which is guaranteed by the current implementation of TaskContext::get_task_stream()), because then all the actual uses of the buffer are done in order on the one stream. It is important that all library CUDA code uses TaskContext::get_task_stream(), and all CUDA operations (including library calls) are enqueued on that stream exclusively. This analysis additionally assumes that no code outside of Legate is concurrently allocating from the eager pool, and that it’s OK for kernels to access a buffer even after it’s technically been deallocated.
Functions
-
template<typename VAL, std::int32_t DIM>
Buffer<VAL, DIM> create_buffer( - const Point<DIM> &extents,
- Memory::Kind kind = Memory::Kind::NO_MEMKIND,
- std::size_t alignment = DEFAULT_ALIGNMENT,
Creates a Buffer of specific extents.
- Parameters:
extents – Extents of the buffer
kind – Kind of the target memory (optional). If not given, the runtime will pick automatically based on the executing processor
alignment – Alignment for the memory allocation (optional)
- Returns:
A Buffer object
-
template<typename VAL>
Buffer<VAL> create_buffer( - std::size_t size,
- Memory::Kind kind = Memory::Kind::NO_MEMKIND,
- std::size_t alignment = DEFAULT_ALIGNMENT,
Creates a Buffer of a specific size. Always returns a 1D Buffer.
-
Scalar null()#
Creates a null
Scalar
Null scalars hold a copy of the singleton “Null”
Type
but hold no physical data or allocation. TheirType
may be queried, but they have zero size, and returnnullptr
whenScalar::ptr()
is called on them. They are useful as tombstone values, or to represent invalid data.- Returns:
A null
Scalar
Variables
-
std::size_t DEFAULT_ALIGNMENT = 16#
The default alignment for memory allocations.
-
class ScopedAllocator#
- #include <legate/data/allocator.h>
A simple allocator backed by Buffer objects.
For each allocation request, this allocator creates a 1D Buffer of
std::int8_t
and returns the raw pointer to it. By default, all allocations are deallocated when the allocator is destroyed, and can optionally be made alive until the task finishes by making the allocator unscoped.Public Functions
- explicit ScopedAllocator(
- Memory::Kind kind,
- bool scoped = true,
- std::size_t alignment = DEFAULT_ALIGNMENT,
Create a
ScopedAllocator
for a specific memory kind.- Parameters:
kind –
Memory::Kind
of the memory on which the Buffer should be createdscoped – If true, the allocator is scoped; i.e., lifetimes of allocations are tied to the allocator’s lifetime. Otherwise, the allocations are alive until the task finishes (and unless explicitly deallocated).
alignment – Alignment for the allocations
- Throws:
std::domain_error – If
alignment
is 0, or not a power of 2.
-
void *allocate(std::size_t bytes)#
Allocates a contiguous buffer of the given
Memory::Kind
When the allocator runs out of memory, the runtime will fail with an error message. Otherwise, the function returns a valid pointer. If
bytes
is0
, returnsnullptr
.See also
- Parameters:
bytes – Size of the allocation in bytes
- Returns:
A raw pointer to the allocation
-
void deallocate(void *ptr)#
Deallocates an allocation.
The input pointer must be one that was previously returned by an
allocate()
call. Ifptr
isnullptr
, this call does nothing.See also
- Parameters:
ptr – Pointer to the allocation to deallocate
- Throws:
std::invalid_argument – If
ptr
was not allocated by this allocator.
-
class Impl#
-
class ExternalAllocation#
- #include <legate/data/external_allocation.h>
Descriptor for external allocations.
An
ExternalAllocation
is a handle to a memory allocation outside Legate’s memory management.ExternalAllocation
objects are used when users want to create Legate stores from the existing allocations external to Legate. (See two overloads ofRuntime::create_store()
that takeExternalAllocation
s.)ExternalAllocation
s can be tagged either read-only or mutable. In case of the latter, Legate guarantees that any updates to the store to which the allocation is attached are also visible via the allocation, wherever the updates are made, at the expense of block-waiting on tasks updating the store. No such propagation of changes happens for read-only external allocations.The client code that creates an external allocation and attaches it to a Legate store must guarantee that the allocation stays alive until all the tasks accessing the store are finished. If the attached allocation was read-only, the code must not mutate the contents of the allocation while the tasks are still running. An external allocation attached to a store can be safely deallocated in two ways:
1) The client code calls the
detach()
method on the store before it dellocate the allocation. Thedetach()
call makes sure that all outstanding operations on the store complete (seeLogicalStore::detach()
). 2) The client code can optionally pass in a deleter for the allocation, which will be invoked once the store is destroyed and the allocation is no longer in use.Deleters don’t need to be idempotent; Legate makes sure that they will be invoked only once on the allocations. Deleters must not throw exceptions (throwable deleters are disallowed by the type system). Deleters need not handle null pointers correctly, as external allocations are not allowed to be created on null pointers. Each deleter is responsible for deallocating only the allocation it is associated with and no other allocations.
Public Types
-
using Deleter = std::function<void(void*)>#
Signature for user-supplied deletion function.
Public Functions
-
bool read_only() const#
Indicates if the allocation is read-only.
- Returns:
true If the allocation is read-only
- Returns:
false Otherwise
-
mapping::StoreTarget target() const#
Returns the kind of memory to which the allocation belongs.
- Returns:
Memory kind in a
mapping::StoreTarget
-
void *ptr() const#
Returns the beginning address of the allocation.
- Returns:
Address to the allocation
-
std::size_t size() const#
Returns the allocation size in bytes.
- Returns:
Allocation size in bytes
Public Static Functions
- static ExternalAllocation create_sysmem(
- void *ptr,
- std::size_t size,
- bool read_only = true,
- std::optional<Deleter> deleter = std::nullopt,
Creates an external allocation for a system memory.
- Parameters:
ptr – Pointer to the allocation
size – Size of the allocation in bytes
read_only – Indicates if the allocation is read-only
deleter – Optional deleter for the passed allocation. If none is given, the user is responsible for the deallocation.
- Throws:
std::invalid_argument – If the
ptr
is null- Returns:
An external allocation
- static ExternalAllocation create_sysmem(
- const void *ptr,
- std::size_t size,
- std::optional<Deleter> deleter = std::nullopt,
Creates a read-only external allocation for a system memory.
- Parameters:
ptr – Pointer to the allocation
size – Size of the allocation in bytes
deleter – Optional deleter for the passed allocation. Passing a deleter means that the ownership of the allocation is transferred to the Legate runtime. If none is given, the user is responsible for the deallocation.
- Throws:
std::invalid_argument – If the
ptr
is null- Returns:
An external allocation
- static ExternalAllocation create_zcmem(
- void *ptr,
- std::size_t size,
- bool read_only = true,
- std::optional<Deleter> deleter = std::nullopt,
Creates an external allocation for a zero-copy memory.
- Parameters:
ptr – Pointer to the allocation
size – Size of the allocation in bytes
read_only – Indicates if the allocation is read-only
deleter – Optional deleter for the passed allocation. Passing a deleter means that the ownership of the allocation is transferred to the Legate runtime. If none is given, the user is responsible for the deallocation.
- Throws:
std::invalid_argument – If the
ptr
is nullstd::runtime_error – If Legate is not configured with CUDA support enabled
- Returns:
An external allocation
- static ExternalAllocation create_zcmem(
- const void *ptr,
- std::size_t size,
- std::optional<Deleter> deleter = std::nullopt,
Creates a read-only external allocation for a zero-copy memory.
- Parameters:
ptr – Pointer to the allocation
size – Size of the allocation in bytes
deleter – Optional deleter for the passed allocation. Passing a deleter means that the ownership of the allocation is transferred to the Legate runtime. If none is given, the user is responsible for the deallocation.
- Throws:
std::invalid_argument – If the
ptr
is nullstd::runtime_error – If Legate is not configured with CUDA support enabled
- Returns:
An external allocation
- static ExternalAllocation create_fbmem(
- std::uint32_t local_device_id,
- void *ptr,
- std::size_t size,
- bool read_only = true,
- std::optional<Deleter> deleter = std::nullopt,
Creates an external allocation for a framebuffer memory.
- Parameters:
local_device_id – Local device ID
ptr – Pointer to the allocation
size – Size of the allocation in bytes
read_only – Indicates if the allocation is read-only
deleter – Optional deleter for the passed allocation. Passing a deleter means that the ownership of the allocation is transferred to the Legate runtime. If none is given, the user is responsible for the deallocation.
- Throws:
std::invalid_argument – If the
ptr
is nullstd::runtime_error – If Legate is not configured with CUDA support enabled
std::out_of_range – If the local device ID is invalid
- Returns:
An external allocation
- static ExternalAllocation create_fbmem(
- std::uint32_t local_device_id,
- const void *ptr,
- std::size_t size,
- std::optional<Deleter> deleter = std::nullopt,
Creates a read-only external allocation for a framebuffer memory.
- Parameters:
local_device_id – Local device ID
ptr – Pointer to the allocation
size – Size of the allocation in bytes
deleter – Optional deleter for the passed allocation. Passing a deleter means that the ownership of the allocation is transferred to the Legate runtime. If none is given, the user is responsible for the deallocation.
- Throws:
std::invalid_argument – If the
ptr
is nullstd::runtime_error – If Legate is not configured with CUDA support enabled
std::out_of_range – If the local device ID is invalid
- Returns:
An external allocation
-
using Deleter = std::function<void(void*)>#
-
class InlineAllocation#
- #include <legate/data/inline_allocation.h>
An object representing the raw memory and strides held by a
PhysicalStore
-
class LogicalArray#
- #include <legate/data/logical_array.h>
A multi-dimensional array.
Subclassed by legate::ListLogicalArray, legate::StringLogicalArray
Public Functions
-
std::uint32_t dim() const#
Returns the number of dimensions of the array.
- Returns:
The number of dimensions
-
const tuple<std::uint64_t> &extents() const#
Returns the extents of the array.
The call can block if the array is unbound
- Returns:
The store’s extents
-
std::size_t volume() const#
Returns the number of elements in the array.
The call can block if the array is unbound
- Returns:
The number of elements in the store
-
bool unbound() const#
Indicates whether the array is unbound.
- Returns:
true
if the array is unbound,false
if it is normal
-
bool nullable() const#
Indicates whether the array is nullable.
- Returns:
true
if the array is nullable,false
otherwise
-
bool nested() const#
Indicates whether the array has child arrays.
- Returns:
true
if the array has child arrays,false
otherwise
-
std::uint32_t num_children() const#
Returns the number of child sub-arrays.
- Returns:
Number of child sub-arrays
- LogicalArray promote(
- std::int32_t extra_dim,
- std::size_t dim_size,
Adds an extra dimension to the array.
The call can block if the array is unbound
- Parameters:
extra_dim – Position for a new dimension
dim_size – Extent of the new dimension
- Throws:
std::invalid_argument – When
extra_dim
is not a valid dimension namestd::runtime_error – If the array or any of the sub-arrays is a list array
- Returns:
A new array with an extra dimension
-
LogicalArray project(std::int32_t dim, std::int64_t index) const#
Projects out a dimension of the array.
The call can block if the array is unbound
- Parameters:
dim – Dimension to project out
index – Index on the chosen dimension
- Throws:
std::invalid_argument – If
dim
is not a valid dimension name orindex
is out of boundsstd::runtime_error – If the array or any of the sub-arrays is a list array
- Returns:
A new array with one fewer dimension
-
LogicalArray slice(std::int32_t dim, Slice sl) const#
Slices a contiguous sub-section of the array.
The call can block if the array is unbound
- Parameters:
dim – Dimension to slice
sl – Slice descriptor
- Throws:
std::invalid_argument – If
dim
is not a valid dimension namestd::runtime_error – If the array or any of the sub-arrays is a list array
- Returns:
A new array that corresponds to the sliced section
-
LogicalArray transpose(const std::vector<std::int32_t> &axes) const#
Reorders dimensions of the array.
The call can block if the array is unbound
- Parameters:
axes – Mapping from dimensions of the resulting array to those of the input
- Throws:
std::invalid_argument – If any of the following happens: 1) The length of
axes
doesn’t match the array’s dimension; 2)axes
has duplicates; 3) Any axis inaxes
is an invalid axis name.std::runtime_error – If the array or any of the sub-arrays is a list array
- Returns:
A new array with the dimensions transposed
- LogicalArray delinearize(
- std::int32_t dim,
- const std::vector<std::uint64_t> &sizes,
Delinearizes a dimension into multiple dimensions.
The call can block if the array is unbound
- Parameters:
dim – Dimension to delinearize
sizes – Extents for the resulting dimensions
- Throws:
std::invalid_argument – If
dim
is invalid for the array orsizes
does not preserve the extent of the chosen dimenisonstd::runtime_error – If the array or any of the sub-arrays is a list array
- Returns:
A new array with the chosen dimension delinearized
-
LogicalStore data() const#
Returns the store of this array.
- Returns:
-
LogicalStore null_mask() const#
Returns the null mask of this array.
- Returns:
-
LogicalArray child(std::uint32_t index) const#
Returns the sub-array of a given index.
- Parameters:
index – Sub-array index
- Throws:
std::invalid_argument – If the array has no child arrays, or the array is an unbound struct array
std::out_of_range – If the index is out of range
- Returns:
-
PhysicalArray get_physical_array() const#
Creates a
PhysicalArray
for thisLogicalArray
This call blocks the client’s control flow and fetches the data for the whole array to the current node
- Returns:
A
PhysicalArray
of theLogicalArray
-
ListLogicalArray as_list_array() const#
Casts this array as a
ListLogicalArray
- Throws:
std::invalid_argument – If the array is not a list array
- Returns:
The array as a
ListLogicalArray
-
StringLogicalArray as_string_array() const#
Casts this array as a
StringLogicalArray
- Throws:
std::invalid_argument – If the array is not a string array
- Returns:
The array as a
StringLogicalArray
-
void offload_to(mapping::StoreTarget target_mem) const#
Offload array to specified target memory.
Copies the array to the specified memory, if necessary, and marks it as the most up-to-date copy, allowing the runtime to discard any copies in other memories.
Main usage is to free up space in one kind of memory by offloading resident arrays and stores to another kind of memory. For example, after a GPU task that reads or writes to an array, users can manually free up Legate’s GPU memory by offloading the array to host memory.
All the stores that comprise the array are offloaded, i.e., the data store, the null mask, and child arrays, etc.
Currently, the runtime does not validate if the target memory has enough capacity or free space at the point of launching or executing the offload operation. The program will most likely crash if there isn’t enough space in the target memory. The user is therefore encouraged to offload to a memory type that is likely to have sufficient space.
This should not be treated as a prefetch call as it offers little benefit to that end. The runtime will ensure that data for a task is resident in the required memory before the task begins executing.
If this array is backed by another array, e.g., if this array is a slice or some other transform of another array, then both the arrays will be offloaded due to being backed by the same memory.
// This snippet launches two GPU tasks that manipulate two different stores, // where each store occupies more than 50% of GPU memory. Runtime can map and // schedule both the tasks at the same time. Without offloading the first store, // mapping will fail for the second task. Therefore, we insert an `offload_to` // call for the first store after submitting the first task and before submitting // the second task. { auto task1 = runtime->create_task(library, GPUonlyTask::TASK_ID); task1.add_output(store1); runtime->submit(std::move(task1)); } store1.offload_to(legate::mapping::StoreTarget::SYSMEM); { auto task2 = runtime->create_task(library, GPUonlyTask::TASK_ID); task2.add_output(store2); runtime->submit(std::move(task2)); }
- Parameters:
target_mem – The target memory.
- Throws:
std::invalid_argument – If Legate was not configured to support
target_mem
.
-
class Impl#
-
std::uint32_t dim() const#
-
class ListLogicalArray : public legate::LogicalArray#
- #include <legate/data/logical_array.h>
A multi-dimensional array representing a list of values.
Public Functions
-
LogicalArray descriptor() const#
Returns the sub-array for descriptors.
- Returns:
Sub-array’s for descriptors
-
LogicalArray vardata() const#
Returns the sub-array for variable size data.
- Returns:
LogicalArray
of variable sized data
-
LogicalArray descriptor() const#
-
class StringLogicalArray : public legate::LogicalArray#
- #include <legate/data/logical_array.h>
A multi-dimensional array representing a string.
Public Functions
-
LogicalArray offsets() const#
Returns the sub-array for offsets.
- Returns:
LogicalArray
of offsets into this array
-
LogicalArray chars() const#
Returns the sub-array for characters.
- Returns:
LogicalArray
representing the characters of the string
-
LogicalArray offsets() const#
-
class LogicalStore#
- #include <legate/data/logical_store.h>
A multi-dimensional data container.
LogicalStore
is a multi-dimensional data container for fixed-size elements. Stores are internally partitioned and distributed across the system. By default, Legate clients need not create nor maintain the partitions explicitly, and the Legate runtime is responsible for managing them. Legate clients can control how stores should be partitioned for a given task by attaching partitioning constraints to the task (see the constraint module for partitioning constraint APIs).Each
LogicalStore
object is a logical handle to the data and is not immediately associated with a physical allocation. To access the data, a client must “map” the store to a physical store (PhysicalStore
). A client can map a store by passing it to a task, in which case the task body can see the allocation, or callingLogicalStore::get_physical_store()
, which gives the client a handle to the physical allocation (seePhysicalStore
for details about physical stores).Normally, a
LogicalStore
gets a fixedShape
upon creation. However, there is a special type of logical stores called “unbound” stores whose shapes are unknown at creation time. (seeRuntime
for the logical store creation API.) The shape of an unbound store is determined by a task that first updates the store; upon the submission of the task, theLogicalStore
becomes a normal store. Passing an unbound store as a read-only argument or requesting aPhysicalStore
of an unbound store are invalid.One consequence due to the nature of unbound stores is that querying the shape of a previously unbound store can block the client’s control flow for an obvious reason; to know the shape of the
LogicalStore
whoseShape
was unknown at creation time, the client must wait until the updater task to finish. However, passing a previously unbound store to a downstream operation can be non-blocking, as long as the operation requires no changes in the partitioning and mapping for theLogicalStore
.Public Functions
-
std::uint32_t dim() const#
Returns the number of dimensions of the store.
- Returns:
The number of dimensions
-
bool has_scalar_storage() const#
Indicates whether the store’s storage is optimized for scalars.
- Returns:
true The store is backed by a scalar storage
- Returns:
false The store is a backed by a normal region storage
-
bool overlaps(const LogicalStore &other) const#
Indicates whether this store overlaps with a given store.
- Returns:
true The stores overlap
- Returns:
false The stores are disjoint
-
const tuple<std::uint64_t> &extents() const#
Returns the extents of the store.
The call can block if the store is unbound
- Returns:
The store’s extents
-
std::size_t volume() const#
Returns the number of elements in the store.
The call can block if the store is unbound
- Returns:
The number of elements in the store
-
bool unbound() const#
Indicates whether the store is unbound.
- Returns:
true
if the store is unbound,false
otherwise
-
bool transformed() const#
Indicates whether the store is transformed.
- Returns:
true
if the store is transformed,false
otherwise
-
LogicalStore reinterpret_as(const Type &type) const#
Reinterpret the underlying data of a
LogicalStore
byte-for-byte as another type.The size and alignment of the new type must match that of the existing type.
The reinterpreted store will share the same underlying storage as the original, and therefore any writes to one will also be reflected in the other. No type conversions of any kind are performed across the stores, the bytes are interpreted as-is. In effect, if one were to model a
LogicalStore
as a pointer to an array, then this routine is equivalent toreinterpret_cast
-ing the pointer.Example:
// Create a store of some shape filled with int32 data. constexpr std::int32_t minus_one = -1; const auto store = runtime->create_store(shape, legate::int32()); runtime->issue_fill(store, legate::Scalar{minus_one}); // Reinterpret the underlying data as unsigned 32-bit integers. auto reinterp_store = store.reinterpret_as(legate::uint32()); // Our new store should have the same type as it was reinterpreted to. ASSERT_EQ(reinterp_store.type(), legate::uint32()); // Our old store still has the same type though. ASSERT_EQ(store.type(), legate::int32()); // Both stores should refer to the same underlying storage. ASSERT_TRUE(store.equal_storage(reinterp_store)); const auto phys_store = reinterp_store.get_physical_store(); const auto acc = phys_store.read_accessor<std::uint32_t, 1>(); std::uint32_t interp_value; // Need to memcpy here in order to do a "true" bitcast. A reinterpret_cast() may or may not // result in the compilers generating the conversion, since type-punning with // reinterpret_cast is UB. std::memcpy(&interp_value, &minus_one, sizeof(minus_one)); for (auto it = legate::PointInRectIterator<1>{phys_store.shape<1>()}; it.valid(); ++it) { ASSERT_EQ(acc[*it], interp_value); }
- Parameters:
type – The new type to interpret the data as.
- Returns:
The reinterpreted store.
- Throws:
std::invalid_argument – If the size (in bytes) of the new type does not match that of the old type.
std::invalid_argument – If the alignment of the new type does not match that of the old type.
- LogicalStore promote(
- std::int32_t extra_dim,
- std::size_t dim_size,
Adds an extra dimension to the store.
Value of
extra_dim
decides where a new dimension should be added, and each dimension \(i\), where \(i\) >=extra_dim
, is mapped to dimension \(i+1\) in a returned store. A returned store provides a view to the input store where the values are broadcasted along the new dimension.For example, for a 1D store
A
contains[1, 2, 3]
,A.promote(0, 2)
yields a store equivalent to:[[1, 2, 3], [1, 2, 3]]
whereas
A.promote(1, 2)
yields:[[1, 1], [2, 2], [3, 3]]
The call can block if the store is unbound
- Parameters:
extra_dim – Position for a new dimension
dim_size – Extent of the new dimension
- Throws:
std::invalid_argument – When
extra_dim
is not a valid dimension name- Returns:
A new store with an extra dimension
-
LogicalStore project(std::int32_t dim, std::int64_t index) const#
Projects out a dimension of the store.
Each dimension \(i\), where \(i\) >
dim
, is mapped to dimension \(i-1\) in a returned store. A returned store provides a view to the input store where the values are on hyperplane \(x_\mathtt{dim} = \mathtt{index}\).For example, if a 2D store
A
contains[[1, 2], [3, 4]]
,A.project(0, 1)
yields a store equivalent to[3, 4]
, whereasA.project(1, 0)
yields[1, 3]
.The call can block if the store is unbound
- Parameters:
dim – Dimension to project out
index – Index on the chosen dimension
- Throws:
std::invalid_argument – If
dim
is not a valid dimension name orindex
is out of bounds- Returns:
A new store with one fewer dimension
-
LogicalStore slice(std::int32_t dim, Slice sl) const#
Slices a contiguous sub-section of the store.
For example, consider a 2D store
A
:[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
A slicing
A.slice(0, legate::Slice{1})
yields[[4, 5, 6], [7, 8, 9]]
The result store will look like this on a different slicing call
A.slice(1, legate::Slice{legate::Slice::OPEN, 2})
:[[1, 2], [4, 5], [7, 8]]
Finally, chained slicing calls
A.slice(0, legate::Slice{1}) .slice(1, legate::Slice{legate::Slice::OPEN, 2})
results in:
[[4, 5], [7, 8]]
The call can block if the store is unbound
- Parameters:
dim – Dimension to slice
sl –
Slice
descriptor
- Throws:
std::invalid_argument – If
dim
is not a valid dimension name- Returns:
A new store that corresponds to the sliced section
-
LogicalStore transpose(std::vector<std::int32_t> &&axes) const#
Reorders dimensions of the store.
Dimension \(i\)i of the resulting store is mapped to dimension
axes[i]
of the input store.For example, for a 3D store
A
[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
transpose calls
A.transpose({1, 2, 0})
andA.transpose({2, 1, 0})
yield the following stores, respectively:[[[1, 5], [2, 6]], [[3, 7], [4, 8]]]
[[[1, 5], [3, 7]], [[2, 6], [4, 8]]]
The call can block if the store is unbound
- Parameters:
axes – Mapping from dimensions of the resulting store to those of the input
- Throws:
std::invalid_argument – If any of the following happens: 1) The length of
axes
doesn’t match the store’s dimension; 2)axes
has duplicates; 3) Any axis inaxes
is an invalid axis name.- Returns:
A new store with the dimensions transposed
- LogicalStore delinearize(
- std::int32_t dim,
- std::vector<std::uint64_t> sizes,
Delinearizes a dimension into multiple dimensions.
Each dimension \(i\) of the store, where \(i >\)
dim
, will be mapped to dimension \(i+N\) of the resulting store, where \(N\) is the length ofsizes
. A delinearization that does not preserve the size of the store is invalid.For example, consider a 2D store
A
[[1, 2, 3, 4], [5, 6, 7, 8]]
A delinearizing call
A.delinearize(1, {2, 2}))
yields:[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
Unlike other transformations, delinearization is not an affine transformation. Due to this nature, delinearized stores can raise
legate::NonInvertibleTransformation
in places where they cannot be used.The call can block if the store is unbound
- Parameters:
dim – Dimension to delinearize
sizes – Extents for the resulting dimensions
- Throws:
std::invalid_argument – If
dim
is invalid for the store orsizes
does not preserve the extent of the chosen dimenison- Returns:
A new store with the chosen dimension delinearized
- LogicalStorePartition partition_by_tiling(
- std::vector<std::uint64_t> tile_shape,
Creates a tiled partition of the store.
The call can block if the store is unbound
- Parameters:
tile_shape – Shape of tiles
- Returns:
A store partition
-
PhysicalStore get_physical_store() const#
Creates a
PhysicalStore
for thisLogicalStore
This call blocks the client’s control flow and fetches the data for the whole store to the current node
- Returns:
A
PhysicalStore
of theLogicalStore
-
void detach()#
Detach a store from its attached memory.
This call will wait for all operations that use the store (or any sub-store) to complete.
After this call returns, it is safe to deallocate the attached external allocation. If the allocation was mutable, the contents would be up-to-date upon the return. The contents of the store are invalid after that point.
-
void offload_to(mapping::StoreTarget target_mem)#
Offload store to specified target memory.
See also
- Parameters:
target_mem – The target memory.
-
bool equal_storage(const LogicalStore &other) const#
Determine whether two stores refer to the same memory.
This routine can be used to determine whether two seemingly unrelated stores refer to the same logical memory region, including through possible transformations in either
this
orother
.The user should note that some transformations do modify the underlying storage. For example, the store produced by slicing will not share the same storage as its parent, and this routine will return false for it:
const auto store = runtime->create_store(legate::Shape{4, 3}, legate::int64()); const auto transformed = store.slice(1, legate::Slice{-2, -1}); // Slices partition a store into a parent and sub-store which both cover distinct regions, // and hence don't share storage. ASSERT_FALSE(store.equal_storage(transformed));
Transposed stores, on the other hand, still share the same storage, and hence this routine will return true for them:
const auto store = runtime->create_store(legate::Shape{4, 3}, legate::int64()); const auto transformed = store.transpose({1, 0}); // Transposing a store doesn't modify the storage ASSERT_TRUE(store.equal_storage(transformed));
- Parameters:
other – The
LogicalStore
to compare with.- Returns:
true
if two stores cover the same underlying memory region,false
otherwise.
-
class Impl#
-
std::uint32_t dim() const#
-
class PhysicalArray#
- #include <legate/data/physical_array.h>
A multi-dimensional array abstraction for fixed- or variable-size elements.
PhysicalArray
s can be backed by one or morePhysicalStore
s, depending on their types.Subclassed by legate::ListPhysicalArray, legate::StringPhysicalArray
Public Functions
-
bool nullable() const noexcept#
Indicates if the array is nullable.
- Returns:
true
if the array is nullable,false
otherwise
-
std::int32_t dim() const noexcept#
Returns the dimension of the array.
- Returns:
Array’s dimension
-
bool nested() const noexcept#
Indicates if the array has child arrays.
- Returns:
true
if the array has child arrays,false
otherwise
-
PhysicalStore data() const#
Returns the store containing the array’s data.
- Throws:
std::invalid_argument – If the array is not a base array
- Returns:
-
PhysicalStore null_mask() const#
Returns the store containing the array’s null mask.
- Throws:
std::invalid_argument – If the array is not nullable
- Returns:
-
PhysicalArray child(std::uint32_t index) const#
Returns the sub-array of a given index.
- Parameters:
index – Sub-array index
- Throws:
std::invalid_argument – If the array has no child arrays
std::out_of_range – If the index is out of range
- Returns:
Sub-
PhysicalArray
at the given index
-
template<std::int32_t DIM>
Rect<DIM> shape() const# Returns the array’s shape.
- Returns:
Array’s shape
-
ListPhysicalArray as_list_array() const#
Casts this array as a
ListPhysicalArray
- Throws:
std::invalid_argument – If the array is not a list array
- Returns:
This array as a
ListPhysicalArray
-
StringPhysicalArray as_string_array() const#
Casts this array as a
StringPhysicalArray
- Throws:
std::invalid_argument – If the array is not a string array
- Returns:
This array as a
StringPhysicalArray
-
bool nullable() const noexcept#
-
class ListPhysicalArray : public legate::PhysicalArray#
- #include <legate/data/physical_array.h>
A multi-dimensional array abstraction for variable-size list of elements.
Public Functions
-
PhysicalArray descriptor() const#
Returns the sub-array for descriptors.
- Returns:
PhysicalArray
of descriptors
-
PhysicalArray vardata() const#
Returns the sub-array for variable size data.
- Returns:
PhysicalArray
of variable sized data
-
PhysicalArray descriptor() const#
-
class StringPhysicalArray : public legate::PhysicalArray#
- #include <legate/data/physical_array.h>
A multi-dimensional array abstraction representing a string.
Public Functions
-
PhysicalArray ranges() const#
Returns the sub-array for ranges.
- Returns:
PhysicalArray
of ranges
-
PhysicalArray chars() const#
Returns the sub-array for characters.
- Returns:
PhysicalArray
of the characters in the string.
-
PhysicalArray ranges() const#
-
class PhysicalStore#
- #include <legate/data/physical_store.h>
A multi-dimensional data container storing task data.
Public Functions
-
template<typename T, std::int32_t DIM, bool VALIDATE_TYPE = LEGATE_TRUE_WHEN_DEBUG>
AccessorRO<T, DIM> read_accessor( Returns a read-only accessor to the store for the entire domain.
- Template Parameters:
T – Element type
DIM – Number of dimensions
VALIDATE_TYPE – If
true
(default), validates type and number of dimensions
- Returns:
A read-only accessor to the store
-
template<typename T, std::int32_t DIM, bool VALIDATE_TYPE = LEGATE_TRUE_WHEN_DEBUG>
AccessorWO<T, DIM> write_accessor( Returns a write-only accessor to the store for the entire domain.
- Template Parameters:
T – Element type
DIM – Number of dimensions
VALIDATE_TYPE – If
true
(default), validates type and number of dimensions
- Returns:
A write-only accessor to the store
-
template<typename T, std::int32_t DIM, bool VALIDATE_TYPE = LEGATE_TRUE_WHEN_DEBUG>
AccessorRW<T, DIM> read_write_accessor( Returns a read-write accessor to the store for the entire domain.
- Template Parameters:
T – Element type
DIM – Number of dimensions
VALIDATE_TYPE – If
true
(default), validates type and number of dimensions
- Returns:
A read-write accessor to the store
-
template<typename OP, bool EXCLUSIVE, std::int32_t DIM, bool VALIDATE_TYPE = LEGATE_TRUE_WHEN_DEBUG>
AccessorRD<OP, EXCLUSIVE, DIM> reduce_accessor( Returns a reduction accessor to the store for the entire domain.
- Template Parameters:
OP – Reduction operator class.
EXCLUSIVE – Indicates whether reductions can be performed in exclusive mode. If
EXCLUSIVE
isfalse
, every reduction via the accessor is performed atomically.DIM – Number of dimensions
VALIDATE_TYPE – If
true
(default), validates type and number of dimensions
- Returns:
A reduction accessor to the store
-
template<typename T, std::int32_t DIM, bool VALIDATE_TYPE = LEGATE_TRUE_WHEN_DEBUG>
AccessorRO<T, DIM> read_accessor(
) const# Returns a read-only accessor to the store for specific bounds.
- Template Parameters:
T – Element type
DIM – Number of dimensions
VALIDATE_TYPE – If
true
(default), validates type and number of dimensions
- Parameters:
bounds – Domain within which accesses should be allowed. The actual bounds for valid access are determined by an intersection between the store’s domain and the bounds.
- Returns:
A read-only accessor to the store
-
template<typename T, std::int32_t DIM, bool VALIDATE_TYPE = LEGATE_TRUE_WHEN_DEBUG>
AccessorWO<T, DIM> write_accessor(
) const# Returns a write-only accessor to the store for the entire domain.
- Template Parameters:
T – Element type
DIM – Number of dimensions
VALIDATE_TYPE – If
true
(default), validates type and number of dimensions
- Parameters:
bounds – Domain within which accesses should be allowed. The actual bounds for valid access are determined by an intersection between the store’s domain and the bounds.
- Returns:
A write-only accessor to the store
-
template<typename T, std::int32_t DIM, bool VALIDATE_TYPE = LEGATE_TRUE_WHEN_DEBUG>
AccessorRW<T, DIM> read_write_accessor(
) const# Returns a read-write accessor to the store for the entire domain.
- Template Parameters:
T – Element type
DIM – Number of dimensions
VALIDATE_TYPE – If
true
(default), validates type and number of dimensions
- Parameters:
bounds – Domain within which accesses should be allowed. The actual bounds for valid access are determined by an intersection between the store’s domain and the bounds.
- Returns:
A read-write accessor to the store
-
template<typename OP, bool EXCLUSIVE, std::int32_t DIM, bool VALIDATE_TYPE = LEGATE_TRUE_WHEN_DEBUG>
AccessorRD<OP, EXCLUSIVE, DIM> reduce_accessor(
) const# Returns a reduction accessor to the store for the entire domain.
- Template Parameters:
OP – Reduction operator class.
EXCLUSIVE – Indicates whether reductions can be performed in exclusive mode. If
EXCLUSIVE
isfalse
, every reduction via the accessor is performed atomically.DIM – Number of dimensions
VALIDATE_TYPE – If
true
(default), validates type and number of dimensions
- Parameters:
bounds – Domain within which accesses should be allowed. The actual bounds for valid access are determined by an intersection between the store’s domain and the bounds.
- Returns:
A reduction accessor to the store
-
template<typename VAL>
VAL scalar() const# Returns the scalar value stored in the store.
The requested type must match with the store’s data type. If the store is not backed by the future, the runtime will fail with an error message.
- Template Parameters:
VAL – Type of the scalar value
- Returns:
The scalar value stored in the store
-
template<typename T, std::int32_t DIM>
Buffer<T, DIM> create_output_buffer(
) const# Creates a Buffer of specified extents for the unbound store.
The returned Buffer is always consistent with the mapping policy for the store. Can be invoked multiple times unless
bind_buffer
is true.
-
template<typename T, std::int32_t DIM>
void bind_data(
) const# Binds a Buffer to the store.
Valid only when the store is unbound and has not yet been bound to another Buffer. The Buffer must be consistent with the mapping policy for the store. Recommend that the Buffer be created by a
create_output_buffer()
call.
- void bind_untyped_data( ) const#
Binds a 1D Buffer of byte-size elements to the store in an untyped manner.
Values in the Buffer are reinterpreted based on the store’s actual type. The Buffer must have enough bytes to be aligned on the store’s element boundary. For example, a 1D Buffer of size 4 wouldn’t be valid if the store had the int64 type, whereas it would be if the store’s element type is int32.
Like the typed counterpart (i.e.,
bind_data()
), the operation is legal only when the store is unbound and has not yet been bound to another buffer. The memory in which the buffer is created must be the same as the mapping decision of this store.Can be used only with 1D unbound stores.
constexpr auto num_elements = 9; const auto element_size_in_bytes = store.type().size(); constexpr auto UNTYPEED_DATA_DIM = 1; auto buffer = legate::create_buffer<std::int8_t, UNTYPEED_DATA_DIM>(num_elements * element_size_in_bytes); store.bind_untyped_data(buffer, legate::Point<UNTYPEED_DATA_DIM>{num_elements});
-
void bind_empty_data() const#
Makes the unbound store empty.
Valid only when the store is unbound and has not yet been bound to another buffer.
-
std::int32_t dim() const#
Returns the dimension of the store.
- Returns:
The store’s dimension
-
template<typename TYPE_CODE = Type::Code>
inline TYPE_CODE code( Returns the type code of the store.
- Returns:
The store’s type code
-
template<std::int32_t DIM>
Rect<DIM> shape() const# Returns the store’s domain.
- Returns:
Store’s domain
-
InlineAllocation get_inline_allocation() const#
Returns a raw pointer and strides to the allocation.
- Returns:
An
InlineAllocation
object holding a raw pointer and strides
-
mapping::StoreTarget target() const#
Returns the kind of memory where this
PhysicalStore
resides.- Throws:
std::invalid_argument – If this function is called on an unbound store
- Returns:
The memory kind
-
bool is_readable() const#
Indicates whether the store can have a read accessor.
- Returns:
true
if the store can have a read accessor,false
otherwise
-
bool is_writable() const#
Indicates whether the store can have a write accessor.
- Returns:
true
if the store can have a write accessor,false
otherwise
-
bool is_reducible() const#
Indicates whether the store can have a reduction accessor.
- Returns:
true
if the store can have a reduction accessor,false
otherwise
-
bool valid() const#
Indicates whether the store is valid.
A store passed to a task can be invalid only for reducer tasks for tree reduction. Otherwise, if the store is invalid, it cannot be used in any data access.
- Returns:
true
if the store is valid,false
otherwise
-
bool transformed() const#
Indicates whether the store is transformed in any way.
- Returns:
true
if the store is transformed,false
otherwise
-
bool is_future() const#
Indicates whether the store is backed by a future (i.e., a container for scalar value)
- Returns:
true
if the store is backed by a future,false
otherwise
-
bool is_unbound_store() const#
Indicates whether the store is an unbound store.
The value DOES NOT indicate that the store has already assigned to a buffer; i.e., the store may have been assigned to a buffer even when this function returns
true
.- Returns:
true
if the store is an unbound store,false
otherwise
-
bool is_partitioned() const#
Indicates whether the store is partitioned.
Tasks sometimes need to know whether a given
PhysicalStore
is partitioned, i.e., corresponds to a subset of the (global)LogicalStore
passed at the launch site. Unless the task explicitly requests broadcasting on theLogicalStore
, the partitioning decision on the store is at the whim of the runtime. In this case, the task can use theis_partitioned()
function to retrieve that information.- Returns:
true
if the store is partitioned,false
otherwise
-
PhysicalStore(const PhysicalArray &array)#
Constructs a store out of an array.
- Throws:
std::invalid_argument – If the array is nullable or has sub-arrays
-
template<typename T, std::int32_t DIM, bool VALIDATE_TYPE = LEGATE_TRUE_WHEN_DEBUG>
-
class Scalar#
- #include <legate/data/scalar.h>
A type-erased container for scalars.
A
Scalar
can be owned or shared, depending on whether it owns the backing allocation: If aScalar
is shared, it does not own the allocation and any of its copies are also shared. If aScalar
is owned, it owns the backing allocation and releases it upon destruction. Any copy of an ownedScalar
is owned as well.Public Functions
-
Scalar(const Type &type, const void *data, bool copy = false)#
Creates a shared
Scalar
with an existing allocation. The caller is responsible for passing in a sufficiently big allocation.- Parameters:
type –
Type
of the scalardata – Allocation containing the data.
copy – If
true
, the scalar copies the data stored in the allocation and becomes owned.
-
template<typename T, typename = std::enable_if_t<!std::is_convertible_v<T, std::string> && !std::is_same_v<std::decay_t<T>, InternalSharedPtr<detail::Scalar>>>>
explicit Scalar( - const T &value,
Creates an owned
Scalar
from a scalar value.- Template Parameters:
T – The scalar type to wrap
- Parameters:
value – A scalar value to create a
Scalar
with
-
template<typename T>
Scalar(const T &value, const Type &type)# Creates an owned
Scalar
of a specified type from a scalar value.
-
explicit Scalar(std::string_view string)#
Creates an owned
Scalar
from astd::string_view
. The value from the original string will be copied.- Parameters:
string – The
std::string_view
to create aScalar
with
-
template<typename T>
explicit Scalar(const std::vector<T> &values)# Creates an owned
Scalar
from astd::vector
of scalars. The values in the input vector will be copied.- Parameters:
values – Values to create a
Scalar
with in a vector
-
template<typename T>
explicit Scalar(const tuple<T> &values)# Creates an owned
Scalar
from atuple
of scalars. The values in the inputtuple
will be copied.- Parameters:
values – Values to create a
Scalar
with in atuple
-
std::size_t size() const#
Returns the size of allocation for the
Scalar
.- Returns:
The size of allocation in bytes
-
template<typename VAL>
VAL value() const# Returns a copy of the value stored in this
Scalar
.1) size of the scalar does not match with size of
VAL
, 2) the scalar holds a string butVAL
isn’tstd:string
orstd:string_view
, or 3) the inverse; i.e.,VAL
isstd:string
orstd:string_view
but the scalar’s type isn’t string
-
template<typename VAL>
Span<const VAL> values() const# Returns values stored in the
Scalar
. If theScalar
does not have a fixed array type, a unit span will be returned.1) the scalar has a fixed array type whose elemenet type has a different size from
VAL
, 2) the scalar holds a string and size ofVAL
isn’t 1 byte, 3) the scalar’s type isn’t a fixed array type and the size is different from size ofVAL
- Throws:
std::invalid_argument – If one of the following cases is encountered:
- Returns:
Values stored in the
Scalar
-
Scalar(const Type &type, const void *data, bool copy = false)#
-
class Shape#
- #include <legate/data/shape.h>
A class to express shapes of multi-dimensional entities in Legate.
Shape
objects describe logical shapes, of multi-dimensional containers in Legate such as Legate arrays and Legate stores. For example, if the shape of a Legate store is(4, 2)
, the store is conceptually a 2D container having four rows and two columns of elements. The shape however does not entail any particular physical manifestation of the container. The aforementioned 2D store can be mapped to an allocation in which the elements of each row would be contiguously located or an allocation in which the elements of each column would be contiguously located.A
Shape
object is essentially a tuple of extents, one for each dimension, and the dimensionality, i.e., the number of dimensions, is the size of this tuple. The volume of theShape
is a product of all the extents.Since Legate allows containers’ shapes to be determined by tasks, some shapes may not be “ready” when the control code tries to introspect their extents. In this case, the control code will be blocked until the tasks updating the containers are complete. This asynchrony behind the shape objects is hidden from the control code and it’s recommended that introspection of the shapes of unbound arrays or stores should be avoided. The blocking behavior of each API call can be found in its reference (methods with no mention of blocking should exhibit no shape-related blocking).
Public Functions
-
inline Shape()#
Constructs a 0D
Shape
The constructed
Shape
is immediately readyEquivalent to
Shape({})
-
Shape(tuple<std::uint64_t> extents)#
Constructs a
Shape
from atuple
of extents.The constructed
Shape
is immediately ready- Parameters:
extents – Dimension extents
-
inline explicit Shape(std::vector<std::uint64_t> extents)#
Constructs a
Shape
from astd::vector
of extents.The constructed
Shape
is immediately ready- Parameters:
extents – Dimension extents
-
inline Shape(std::initializer_list<std::uint64_t> extents)#
Constructs a
Shape
from astd::initializer_list
of extents.The constructed
Shape
is immediately ready- Parameters:
extents – Dimension extents
-
const tuple<std::uint64_t> &extents() const#
Returns the
Shape
’s extents.If the
Shape
is of an unbound array or store, the call blocks until the shape becomes ready.- Returns:
Dimension extents
-
std::size_t volume() const#
Returns the
Shape
’s volume.Equivalent to
extents().volume()
. If theShape
is of an unbound array or store, the call blocks until theShape
becomes ready.- Returns:
Volume of the
Shape
-
std::uint32_t ndim() const#
Returns the number of dimensions of this
Shape
Unlike other
Shape
-related queries, this call is non-blocking.- Returns:
Number of dimensions
-
inline std::uint64_t operator[](std::uint32_t idx) const#
Returns the extent of a given dimension.
If the
Shape
is of an unbound array or store, the call blocks until theShape
becomes ready. UnlikeShape::at()
, this method does not check the dimension index.- Parameters:
idx – Dimension index
- Returns:
Extent of the chosen dimension
-
inline std::uint64_t at(std::uint32_t idx) const#
Returns the extent of a given dimension.
If the
Shape
is of an unbound array or store, the call blocks until theShape
becomes ready.- Parameters:
idx – Dimension index
- Throws:
std::out_of_range – If the dimension index is invalid
- Returns:
Extent of the chosen dimension
-
std::string to_string() const#
Generates a human-readable string from the
Shape
(non-blocking)- Returns:
std::tring
generated from theShape
-
inline Shape()#
-
class Slice#
- #include <legate/data/slice.h>
A slice descriptor.
Slice
behaves similarly to how the slice in Python does, and has different semantics fromstd::slice
.Public Functions
- inline Slice(
- std::optional<std::int64_t> _start = OPEN,
- std::optional<std::int64_t> _stop = OPEN,
Constructs a
Slice
If provided (and not
Slice::OPEN
),_start
must compare less than or equal to_stop
. Similarly, if provided (and notSlice::OPEN
),_stop
must compare greater than or equal to_start
. Put simply, unless one or both of the ends are unbounded,[_start, _stop]
must form a valid (possibly empty) interval.- Parameters:
_start – The optional begin index of the slice, or
Slice::OPEN
if the start of the slice is unbounded._stop – The optional stop index of the slice, or
Slice::OPEN
if the end of the slice if unbounded.
-
template<typename T>
class Span# - #include <legate/utilities/span.h>
A simple span implementation used in Legate.
Should eventually be replaced with std::span once we bump up the C++ standard version to C++20
Public Functions
-
template<typename C, typename = std::enable_if_t<detail::is_container_v<C> && !std::is_same_v<C, std::initializer_list<T>>>>
constexpr Span( - C &container,
Construct a span from a container-like object.
This overload only participates in overload resolution if C satisfies ContainerLike. It must have a valid overload of
std::data()
andstd::size()
which refer to a contiguous buffer of data and its size respectively.- Parameters:
container – The container-like object.
-
constexpr Span(std::initializer_list<T> il)#
Construct a span from an initializer list of items directly.
This overload is relatively dangerous insofar that the span can very easily outlive the initializer list. It is generally only preferred to target this overload when taking a
Span
as a function argument where the ability to simply dofoo({1, 2, 3, 4})
is preferred.- Parameters:
il – The initializer list.
-
constexpr Span(T *data, std::size_t size)#
Creates a span with an existing pointer and a size.
The caller must guarantee that the allocation is big enough (i.e., bigger than or equal to
sizeof(T) * size
) and that the allocation is alive while the span is alive.- Parameters:
data – Pointer to the data
size – Number of elements
-
std::size_t size() const#
Returns the number of elements.
- Returns:
The number of elements
-
const T *begin() const#
Returns the pointer to the first element.
- Returns:
Pointer to the first element
-
const T *end() const#
Returns the pointer to the end of allocation.
- Returns:
Pointer to the end of allocation
-
template<typename C, typename = std::enable_if_t<detail::is_container_v<C> && !std::is_same_v<C, std::initializer_list<T>>>>
-
LEGATE_TRUE_WHEN_DEBUG#