Loop variables and their associated iteration spaces are fundamental to writing loop kernels in RAJA. RAJA provides some basic iteration space types that serve as flexible building blocks that can be used to form a variety of loop iteration patterns. These types can be used to define a particular order for loop iterates, aggregate and partition iterates, as well as other configurations. In this section, we introduce RAJA index and iteration space concepts and types.
Note
All RAJA iteration space types described here are located in the
namespace RAJA.
Please see the following tutorial sections for detailed examples that use RAJA iteration space concepts:
Just like traditional C and C++ for-loops, RAJA uses index variables to
identify loop iterates. Any lambda expression that represents all or part of
a loop body passed to a RAJA::forall or RAJA::kernel method will
take at least one loop index variable argument. RAJA iteration space types
are templates that allow users to use any integral type for an
index variable.
A RAJA Segment represents a set of indices that one wants to execute as a unit for a kernel. RAJA provides the following Segment types:
RAJA::TypedRangeSegmentrepresents a stride-1 rangeRAJA::TypedRangeStrideSegmentrepresents a (non-unit) stride rangeRAJA::TypedListSegmentrepresents an arbitrary set of indices
A RAJA::TypedIndexSet is a container that can hold an arbitrary collection
of segments to compose iteration patterns in a single kernel invocation.
Segment and IndexSet types are used in RAJA::forall and other RAJA kernel
execution mechanisms to define the iteration space for a kernel.
Note
Iterating over the indices of all segments in a RAJA index set requires a two-level execution policy, with two template parameters, as shown above. The first parameter specifies how to iterate over the segments. The second parameter specifies how each segment will execute. See :ref:`indexsetpolicy-label` for more information about RAJA index set execution policies.
Note
It is the responsibility of the user to ensure that segments are defined properly when using RAJA index sets. For example, if the same index appears in multiple segments, the corresponding loop iteration will be run multiple times.
Please see :ref:`tut-indexset-label` for a detailed discussion of how to create and use these segment types.
It is worth noting that RAJA segment types model C++ iterable interfaces. In particular, each segment type defines three methods:
- begin()
- end()
- size()
and two types:
- iterator (essentially a random access iterator type)
- value_type
Thus, any iterable type that defines these methods and types appropriately can be used as a segment with RAJA kernel execution templates.
RAJA also provides RAJA::range(...) helpers that construct the segment
type for common half-open iteration patterns. These helpers are intended to
mirror the shape of Python's range while returning RAJA segment objects
that can be passed directly to RAJA::forall and other execution
interfaces.
The supported forms are:
RAJA::range(end) // [0, end) RAJA::range<IndexT>(end) // [0, end) with explicit storage type RAJA::range(begin, end) // [begin, end) RAJA::range(begin, end, stride) // [begin, end) with stride
The return type depends on the arguments:
RAJA::range(end)andRAJA::range(begin, end)return aRAJA::TypedRangeSegment.RAJA::range(begin, end, stride)returns aRAJA::TypedRangeStrideSegment.- When one of the bounds is a RAJA strong index type, such as a type created
with
RAJA_INDEX_VALUE, that strong type is preserved for the loop variable when possible. - Providing an explicit template argument, such as
RAJA::range<MyIndex>(end), overrides the deduced storage type.
For example:
RAJA_INDEX_VALUE(CellIndex, "CellIndex");
RAJA::forall<RAJA::seq_exec>(RAJA::range(N), [=](RAJA::Index_type i) {
values[i] = i * i;
});
RAJA::forall<RAJA::seq_exec>(RAJA::range<CellIndex>(N), [=](CellIndex i) {
typed_values[*i] = *i + 10;
});
RAJA::forall<RAJA::seq_exec>(RAJA::range(2, 6), [=](int i) {
subrange_values[i] = i;
});
RAJA::forall<RAJA::seq_exec>(RAJA::range(CellIndex {1}, N, 2),
[=](CellIndex i) {
strided_values[*i] = *i;
});
Strided ranges follow the same half-open interval convention as
RAJA::TypedRangeStrideSegment. Positive strides move forward, and negative
strides move backward. For example, RAJA::range(N - 1, -1, -2) visits
N - 1, N - 3, ... down to the first value that remains greater than
-1. A zero stride is invalid and causes RAJA to abort or throw, depending
on the build configuration.
The older RAJA::make_range and RAJA::make_strided_range helpers remain
available. Use RAJA::range(...) when the Python-like spelling improves
readability or when you want the one-argument [0, end) shorthand.
The complete example added in this branch is shown below:
.. literalinclude:: ../../../../examples/raja-ranges.cpp :language: c++