Intel® oneAPI DPC++/C++ Compiler Developer Guide and Reference

ID 767253
Date 3/22/2024
Public

A newer version of this document is available. Customers should click here to go to the newest version.

Document Table of Contents

Accessor Concept

Accessor and const_accessor objects obtained via n_container::access() and n_container::const_access() provide access to read from or write to cells inside an n_container.

Syntax

The following methods return objects meeting the requirements of the accessor concept.

auto n_container::access();
auto n_container::const_access();
auto accessor_concept::section(n_bounds_t<…>);
auto accessor_concept::translated_to(n_index_t<…>);
auto accessor_concept::translated_to_zero();

Description

Accessor objects provide read/write access to individual cells of an n-dimensional container. Index values passed to a sequence of array subscript operator calls will produce a proxy concept that can import to or export the primitive data the corresponding cell inside the container.

auto image = make_n_container<MyStruct, layout::soa>(n_extent[128][256]);
auto acc = image.access();
MyStruct in_value(100.0f, 200.0f, 300.0f);

acc[64][128] = in_value;
MyStruct out_value = acc[64][128];

assert(out_value == in_value);
Accessors also know their valid iteration space, which can queried using the template function bound_d<int DimensionT>(accessor).

assert(bounds_d<0>(acc) == bounds(0_fixed,128));
assert(bounds_d<1>(acc) == bounds(0_fixed,256));

An accessor may have a non-zero index space if it has a translation embedded into it, bounds_d will reflect any such translation.

auto shifted_acc = acc.translated_to(n_index[1000][2000]);
assert(bounds_d<0>(shifted_acc) == bounds(1000,1128));
assert(bounds_d<1>(shifted_acc) == bounds(2000,2256));

This is useful to have a smaller sized container participate in a calculation over a portion of a larger index space, simplifying programming as the same index variable can be used, and the accessor takes care of applying the necessary translation. An accessor may represent a subsection over the original extents, bounds_d will identify the valid iteration space for that accessor.

auto subsection_acc = a.section(n_bounds[bounds(64,96)][bounds(128,160)]);
assert(bounds_d<0>(subsection_acc) == bounds(64, 96));
assert(bounds_d<1>(subsection_acc) == bounds(128, 160);

It can also be useful to have subsections be translated back to start their iteration space at 0. For efficiency, the translated_to_zero() method is provided to create an accessor shifted back to zero.

auto zb_sub_acc = a.section( n_bounds[bounds(64, 96)][bounds(128, 160)] ).translated_to_zero();
assert(bounds_d<0>(zb_sub_acc) == bounds(0, 32));
assert(bounds_d<1>(zb_sub_acc) == bounds(0, 32));

If fewer array subscript calls applied to an accessor than its rank, the result is another accessor of a lower rank. This can be useful to obtain accessors suitable to pass to code expecting lower rank accessors. Such as a obtaining a 3d accessor from a 4d container by specifying only a single index via array subscript. This has the effect of embedding the index value of the dimension inside accessor. When the final dimension is sliced, the result is a proxy object to the cell inside the container corresponding to the embedded index values inside the sliced accessors

auto image4d = make_n_container<MyStruct, layout::soa>(n_extent[10][20][128][256]);

MyStruct in_value(100.0f, 200.0f, 300.0f);
auto acc4d = image4d.access();
auto acc3d = acc4d[5];
auto acc2d = acc3d[10];
auto acc1d = acc2d[64];
acc1d[128] = in_value;
MyStruct out_value = acc4d[5][10][64][128];
assert(out_value == in_value);

The following table provides information on the requirements of the accessor concept.

Pseudo-Signature

Description

typedef PrimitiveT primitive_type;

Data type inside the cells of the container.

static constexpr int rank;

Number of free dimensions of accessor

accessor_concept(const accessor_concept &a_other)

Effects: constructs a copy of another accessor of the exact same type

template<typename IndexT>
element_concept operator[] (const IndexT a_index) const

Requirements: rank == 1 and IndexT is one of: int, aligned<AlignmentT>, fixed<NumberT>, linear_index, or simd_index<LaneCountT>

Effects: When only 1 free dimension is left, the operator[] will construct an element_concept which is the proxy to the cell inside the container. If this accessor was obtained with const_access(), then the proxy will provide read only interface to the cell’s data.

Returns: The proxy object to cell inside the container corresponding to the position identified by the a_index along with any embedded index values for other dimensions

template<typename IndexT>
accessor_concept operator[] (const IndexT a_index) const 

Requirements: rank > 1 and IndexT is one of: int, aligned<AlignmentT>, fixed<NumberT>, linear_index, or simd_index<LaneCountT>

Effects: When 2 or more free dimensions are left, the operator[] will construct another accessor_concept of lower rank embeding a_index inside of it, effectively fixing that dimension’s index value for any accesses made through the returned accessor_concept.

Returns: The accessor_concept of lower rank ( one less free dimension).

template<int DimensionT>
auto bounds_d() const

Requirements: DimensionT >=0 and DimensionT < rank

Effects: Determine the bounds of a free dimension using DimensionT as a 0 based index starting at the leftmost dimension.

Returns: bounds_t of the DimensionT

auto bounds_dXX() const 
where XX is 0-19

Requirements: XX >=0 and XX < rank and XX < 20

Effects: Non templated methods to determine the bounds of a free dimension using XX as a 0 based index starting at the leftmost dimension.

Returns: bounds_t of the XX dimension

template<int DimensionT>
auto extent_d() const

Requirements: DimensionT >=0 and DimensionT < rank

Effects: Determine the extent of a free dimension using DimensionT as a 0 based index starting at the leftmost dimension.

Returns: extent of the DimensionT

auto extent_dXX() const 
where XX is 0-19

Requirements: XX >=0 and XX < rank and XX < 20

Effects: Non templated methods to determine the extent of a free dimension using XX as a 0 based index starting at the leftmost dimension.

Returns: extent of the XX dimension

template<typename ...IndexListT>
accessor_concept translated_to(
    n_index_t<IndexListT...> a_n_index) const

Requirements: a_n_index has same rank as the accessor

Effects: construct an accessor_concept with an embedded translation such that accessing a_n_index will corresponds back to the current lower bounds. Easy way to think of it is that current iteration space is translated to a_n_index space.

Returns: accessor_concept whose bounds have the same extents, but whose lower bounds start at the supplied a_n_index

template<typename ...IndexListT>
accessor_concept translated_to_zero() const

Effects: construct an accessor_concept with an embedded translation such that accessing [0] index for all dimensions will corresponds back to the current lower bounds. Easy way to think of it is that current iteration space is translated to [0] for all free dimensions.

Returns: accessor_concept whose bounds have the same extents, but whose lower bounds start [0]…[0]

template<typename ...BoundsTypeListT>
    auto
    section(const n_bounds_t<BoundsTypeListT...> &a_n_bounds) const

Requirements: a_n_bounds has same rank as the accessor and a_n_bounds is contained by the accessors current bounds.

Effects: construct an accessor_concept with using the supplied a_n_bounds to represent its valid iteration space. Because a_n_bounds must be contained within the existing bounds, we are effictively creating an accessor over a section of the container. Easy way to think of it is that current bounds are being restricted to a_n_bounds. Note: can be useful to chain a call translated_to_zero() on to the return value.

Returns:accessor_concept whose bounds are set to the supplied a_n_bounds