Memory ownership and gko::array#
gko::array<T> is the basic owning container for contiguous data in Ginkgo. Every higher-level container — vectors, matrices, solver workspaces — is built on it. This page explains how array owns memory, what its copy and move semantics are, and how it interacts with executors. View semantics — wrapping memory you do not own — are covered separately in Views and zero-copy wrapping.
What gko::array stores#
array<T> is templated by the storage type. The type must be a POD type (plain old data): a built-in type, or a struct of built-in types. Examples include double, float, int, std::complex<double>. The type may not be const-qualified — use a const array<T> to express read-only access to the whole container, rather than array<const T>.
Attention
Some functionality — notably fill — is not available for all storage types. The restriction is enforced at link time. If you use an unsupported type, the error message will look like:
undefined reference to gko::array<T>::fill(T)
The supported types are documented in the Resizing and filling section below.
Construction#
An array is constructed from an executor and the number of stored elements:
std::shared_ptr<const gko::Executor> exec = ...;
gko::size_type size = ...;
gko::array<double> a(exec, size);
Attention
After construction with a size only, the stored memory is not initialized. Reading from a before writing to it is undefined behaviour.
To create an array that is already filled with data, pass an initializer list instead of a size. The initializer list lives in CPU memory, but the created array will place the elements into the memory space of the executor:
gko::array<double> b(exec, {1.0, 2.0, 3.0});
An alternative is to provide begin and end iterators from any host container. The data is copied into the array:
std::vector<double> std_c{1.0, 2.0, 3.0};
gko::array<double> c(exec, std_c.begin(), std_c.end());
All four common construction forms together:
// Size only — uninitialised
gko::array<double> a(exec, size);
// Initializer list
gko::array<double> b(exec, {1.0, 2.0, 3.0});
// Iterator pair from host container
std::vector<double> std_c{1.0, 2.0, 3.0};
gko::array<double> c(exec, std_c.begin(), std_c.end());
// Default construction
gko::array<double> d; // no executor, zero size
gko::array<double> e(exec); // executor, zero size
The default-constructed array d is not associated with any executor. The empty array e is associated with exec but stores nothing. The difference matters for copy and move operations.
Accessing the data#
The primary access point is the pair get_data() / get_const_data():
double* ptr = a.get_data(); // non-const only
const double* const_ptr = a.get_const_data(); // always available
get_data() is unavailable on const arrays. Both return nullptr if the array is empty.
Note
The pattern of having get_<something> and get_const_<something> — mutable and const accessors — is standard throughout Ginkgo. For const objects, only the const accessor is callable, making accidental mutation near-impossible.
Attention
The returned pointers address the executor’s memory space. If the array lives on a CudaExecutor, the pointer is a device pointer that is not dereferenceable on the host. Dereference the pointer only on the device that owns it.
Element count and executor:
gko::size_type n = a.get_size(); // number of stored elements
auto e = a.get_executor(); // shared_ptr to the executor; nullptr if default-constructed
Copy and move semantics#
A key rule: copy and move assignment never change the executor of the destination array. This makes cross-device memory movement simple and explicit.
Operation |
Behaviour |
|---|---|
|
Copy. |
|
Cross-executor copy — copies |
|
Move. |
|
Generic helper that returns a deep copy on |
The executor-conservation rule means that assigning across executors performs a cross-device copy automatically:
gko::array<double> a_host(host_exec, {1.0, 2.0, 3.0});
gko::array<double> a_gpu(gpu_exec, 3); // gpu array, uninitialised
a_gpu = a_host; // copies host → device; a_gpu remains on gpu_exec
Cross-executor transfers#
The canonical way to move data from host to device (or back) is via the cross-executor constructor:
auto a_host = gko::array<double>(host_exec, {1.0, 2.0, 3.0});
auto a_gpu = gko::array<double>(gpu_exec, a_host); // copy host → device
For generic code that copies any Ginkgo object across executors, use gko::clone:
auto a_gpu = gko::clone(gpu_exec, a_host);
To change the executor associated with an existing array, use set_executor. If the new executor differs from the current one, the data is automatically migrated:
gko::array<double> a(omp_exec, {1.0, 2.0, 3.0});
a.set_executor(hip_exec); // values [1.0, 2.0, 3.0] are now in GPU memory
Const-correctness#
Prefer const array<T> over array<const T>. A const array<T> disables get_data(), fill(), resize_and_reset(), and set_executor() — enforcing the read-only contract at compile time. array<const T> is not supported.
Resizing and filling#
Resize an array and discard its contents with resize_and_reset:
a.resize_and_reset(new_size); // content is undefined after this call
Attention
resize_and_reset throws if the array has no executor (default-constructed without one) or if the array is a non-owning view. See Views and zero-copy wrapping for the view restriction.
Reset the size to zero:
a.clear(); // get_data() returns nullptr afterward
Set every element to the same value with fill:
gko::array<double> a(exec, 10);
a.fill(1.5); // all 10 elements are now 1.5
fill is only available for the following storage types:
Real and complex floating-point:
float,double,std::complex<float>,std::complex<double>Index and size types:
int32,int64,size_type
Using fill with any other type results in a link-time error. This restriction exists because fill dispatches to a GPU kernel that must be instantiated at compile time for each supported type.
What array does not do#
array provides raw contiguous storage, not arithmetic. It does not support element-wise addition, scaling, or dot products. For arithmetic on numeric vectors, use gko::matrix::Dense<T> — a LinOp built on top of array that exposes full linear algebra operations.
array does not support copy-on-write semantics, reference counting of the stored data, or RAII for non-POD types. For owning LinOp objects, use std::shared_ptr<gko::LinOp>.
Wrapping memory you do not own#
Owning memory is the common case, but array also supports a non-owning view
mode that wraps memory you already control — a buffer from deal.II, MFEM, Kokkos,
or a raw user pointer — without copying it. The lifetime contract, integration
patterns (deal.II distributed vector, raw user buffers, Kokkos View,
zero-copy CSR construction), and “when to copy instead of view” guidance live on
their own page:
See also
The Executor model — what an
arrayis bound to.API reference:
gko::array