4.8. Memory Layout of Array Handles
Chapter 3.2 (Basic Array Handles) describes the basics of the viskores::cont::ArrayHandle class, which is the interface to the arrays of data that Viskores operates on.
Recall that viskores::cont::ArrayHandle is a templated class with two template parameters.
The first template argument is the type of each item in the array.
The second parameter, which is optional, determines how the array is stored in memory.
This can be used in a variety of different ways, but its primary purpose is to provide a strategy for laying the data out in memory.
This chapter documents the ways in which Viskores can store and access arrays of data in different layouts.
4.8.1. Basic Memory Layout
If the second storage template parameter of viskores::cont::ArrayHandle is not specified, it defaults to the basic memory layout.
This is roughly synonymous with a wrapper around a standard C array, much like std::vector.
In fact, Section 3.2.1 (Creating Array Handles) provides examples of wrapping a default viskores::cont::ArrayHandle around either a basic C array or a std::vector.
Viskores provides viskores::cont::ArrayHandleBasic as a convenience class for working with basic array handles.
viskores::cont::ArrayHandleBasic is a simple subclass of viskores::cont::ArrayHandle with the default storage in the second template argument (which is viskores::cont::StorageTagBasic).
viskores::cont::ArrayHandleBasic and its superclass can be used more or less interchangeably.
-
template<typename T>
class ArrayHandleBasic : public viskores::cont::ArrayHandle<T, viskores::cont::StorageTagBasic> Basic array storage for an array handle.
This array handle references a standard C array. It provides a level of safety and management across devices. This is the default used when no storage is specified. Using this subclass allows access to the underlying raw array.
Public Functions
-
inline const T *GetReadPointer() const
Gets raw access to the
ArrayHandle’s data.Note that the returned array may become invalidated by other operations on the ArryHandle.
-
inline const T *GetReadPointer(viskores::cont::Token &token) const
Gets raw access to the
ArrayHandle’s data.- Parameters:
token – When a
viskores::cont::Tokenis provided, the array is locked from being used by any write operations until the token goes out of scope.
-
inline T *GetWritePointer() const
Gets raw write access to the
ArrayHandle’s data.Note that the returned array may become invalidated by other operations on the ArryHandle.
-
inline T *GetWritePointer(viskores::cont::Token &token) const
Gets raw write access to the
ArrayHandle’s data.- Parameters:
token – When a
viskores::cont::Tokenis provided, the array is locked from being used by any read or write operations until the token goes out of scope.
-
inline const T *GetReadPointer(viskores::cont::DeviceAdapterId device) const
Gets raw access to the
ArrayHandle’s data on a particular device.Note that the returned array may become invalidated by other operations on the ArryHandle.
- Parameters:
device – The device ID or device tag specifying on which device the array will be valid on.
-
inline const T *GetReadPointer(viskores::cont::DeviceAdapterId device, viskores::cont::Token &token) const
Gets raw access to the
ArrayHandle’s data.- Parameters:
device – The device ID or device tag specifying on which device the array will be valid on.
token – When a
viskores::cont::Tokenis provided, the array is locked from being used by any write operations until the token goes out of scope.
-
inline T *GetWritePointer(viskores::cont::DeviceAdapterId device) const
Gets raw write access to the
ArrayHandle’s data.Note that the returned array may become invalidated by other operations on the ArryHandle.
- Parameters:
device – The device ID or device tag specifying on which device the array will be valid on.
-
inline T *GetWritePointer(viskores::cont::DeviceAdapterId device, viskores::cont::Token &token) const
Gets raw write access to the
ArrayHandle’s data.- Parameters:
device – The device ID or device tag specifying on which device the array will be valid on.
token – When a
viskores::cont::Tokenis provided, the array is locked from being used by any read or write operations until the token goes out of scope.
-
inline const T *GetReadPointer() const
Because a viskores::cont::ArrayHandleBasic represents arrays as a standard C array, it is possible to get a pointer to this array using either viskores::cont::ArrayHandleBasic::GetReadPointer() or viskores::cont::ArrayHandleBasic::GetWritePointer().
1void LegacyFunction(const int* data);
2
3void UseArrayWithLegacy(const viskores::cont::ArrayHandle<viskores::Int32> array)
4{
5 viskores::cont::ArrayHandleBasic<viskores::Int32> basicArray = array;
6 viskores::cont::Token token; // Token prevents array from changing while in scope.
7 const int* cArray = basicArray.GetReadPointer(token);
8 LegacyFunction(cArray);
9 // When function returns, token goes out of scope and array can be modified.
10}
Did You Know?
When you get an array pointer this way, the viskores::cont::ArrayHandle still has a reference to it.
If using multiple threads, you can use a viskores::cont::Token object to lock the array.
When the token is used to get a pointer, it will lock the array as long as the token exists.
Example 4.73 demonstrates using a viskores::cont::Token.
4.8.2. Structure of Arrays
The basic viskores::cont::ArrayHandle stores viskores::Vec objects in sequence.
In this sense, a basic array is an Array of Structures (AOS).
Another approach is to store each component of the structure (i.e., the viskores::Vec) in a separate array.
This is known as a Structure of Arrays (SOA).
There are advantages to this approach including potentially better cache performance and the ability to combine arrays already represented as separate components without copying them.
Arrays of this nature are represented with a viskores::cont::ArrayHandleSOA, which is a subclass of viskores::cont::StorageTagSOA.
-
template<typename T>
class ArrayHandleSOA : public viskores::cont::ArrayHandle<T, viskores::cont::StorageTagSOA> An
ArrayHandlethat for Vecs stores each component in a separate physical array.ArrayHandleSOAbehaves like a regularArrayHandle(with a basic storage) except that if you specify aValueTypeof aVecor aVec-like, it will actually store each component in a separate physical array. When data are retrieved from the array, they are reconstructed intoVecobjects as expected.The intention of this array type is to help cover the most common ways data is lain out in memory. Typically, arrays of data are either an “array of structures” like the basic storage where you have a single array of structures (like
Vec) or a “structure of arrays” where you have an array of a basic type (likefloat) for each component of the data being represented. TheArrayHandleSOAmakes it easy to cover this second case without creating special types.ArrayHandleSOAcan be constructed from a collection ofArrayHandlewith basic storage. This allows you to constructVecarrays from components without deep copies.Public Functions
-
inline ArrayHandleSOA(const std::array<ComponentArrayType, NUM_COMPONENTS> &componentArrays)
Construct an
ArrayHandleSOAfrom a collection of component arrays.viskores::cont::ArrayHandle<T> components1; viskores::cont::ArrayHandle<T> components2; viskores::cont::ArrayHandle<T> components3; // Fill arrays... std::array<viskores::cont::ArrayHandle<T>, 3> allComponents{ components1, components2, components3 }; viskores::cont::ArrayHandleSOA<viskores::Vec<T, 3>> vecarray(allComponents);
-
inline ArrayHandleSOA(const std::vector<ComponentArrayType> &componentArrays)
Construct an
ArrayHandleSOAfrom a collection of component arrays.viskores::cont::ArrayHandle<T> components1; viskores::cont::ArrayHandle<T> components2; viskores::cont::ArrayHandle<T> components3; // Fill arrays... std::vector<viskores::cont::ArrayHandle<T>> allComponents{ components1, components2, components3 }; viskores::cont::make_ArrayHandleSOA<viskores::Vec<T, 3>> vecarray(allComponents);
-
inline ArrayHandleSOA(std::initializer_list<ComponentArrayType> &&componentArrays)
Construct an
ArrayHandleSOAfrom a collection of component arrays.viskores::cont::ArrayHandle<T> components1; viskores::cont::ArrayHandle<T> components2; viskores::cont::ArrayHandle<T> components3; // Fill arrays... viskores::cont::ArrayHandleSOA<viskores::Vec<T, 3> vecarray( { components1, components2, components3 });
-
inline ArrayHandleSOA(std::initializer_list<std::vector<ComponentType>> &&componentVectors)
Construct an
ArrayHandleSOAfrom a collection of component arrays.The data is copied from the
std::vectors to the array handle.std::vector<T> components1; std::vector<T> components2; std::vector<T> components3; // Fill arrays... viskores::cont::ArrayHandleSOA<viskores::Vec<T, 3>> vecarray( { components1, components2, components3 });
-
template<typename Allocator, typename ...RemainingVectors>
inline ArrayHandleSOA(viskores::CopyFlag copy, const std::vector<ComponentType, Allocator> &vector0, RemainingVectors&&... componentVectors) Construct an
ArrayHandleSOAfrom a collection of component arrays.The first argument is a
viskores::CopyFlagto determine whether the input arrays should be copied. The component arrays are listed as arguments. This only works if all the templated arguments are of typestd::vector<ComponentType>.std::vector<T> components1; std::vector<T> components2; std::vector<T> components3; // Fill arrays... viskores::cont::ArrayHandleSOA<viskores::Vec<T, 3>> vecarray( viskores::CopyFlag::On, components1, components2, components3);
-
template<typename ...RemainingVectors>
inline ArrayHandleSOA(viskores::CopyFlag copy, std::vector<ComponentType> &&vector0, RemainingVectors&&... componentVectors) Construct an
ArrayHandleSOAfrom a collection of component arrays.The first argument is a
viskores::CopyFlagto determine whether the input arrays should be copied. The component arrays are listed as arguments. This only works if all the templated arguments are rvalues of typestd::vector<ComponentType>.std::vector<T> components1; std::vector<T> components2; std::vector<T> components3; // Fill arrays... viskores::cont::ArrayHandleSOA<viskores::Vec<T, N> vecarray(viskores::CopyFlag::Off, std::move(components1), std::move(components2), std::move(components3);
-
inline ArrayHandleSOA(std::initializer_list<const ComponentType*> componentArrays, viskores::Id length, viskores::CopyFlag copy)
Construct an
ArrayHandleSOAfrom a collection of component arrays.T* components1; T* components2; T* components3; // Fill arrays... viskores::cont::ArrayHandleSOA<viskores::Vec<T, 3>>( { components1, components2, components3 }, size, viskores::CopyFlag::On);
-
template<typename ...RemainingArrays>
inline ArrayHandleSOA(viskores::Id length, viskores::CopyFlag copy, const ComponentType *array0, const RemainingArrays&... componentArrays) Construct an
ArrayHandleSOAfrom a collection of component arrays.The component arrays are listed as arguments. This only works if all the templated arguments are of type
ComponentType*.T* components1; T* components2; T* components3; // Fill arrays... viskores::cont::ArrayHandleSOA<viskores::Vec<T, 3>> vecarray( size, viskores::CopyFlag::On, components1, components2, components3);
-
inline viskores::cont::ArrayHandleBasic<ComponentType> GetArray(viskores::IdComponent index) const
Get a basic array representing the component for the given index.
-
inline void SetArray(viskores::IdComponent index, const ComponentArrayType &array)
Replace a component array.
-
inline ArrayHandleSOA(const std::array<ComponentArrayType, NUM_COMPONENTS> &componentArrays)
viskores::cont::ArrayHandleSOA can be constructed and allocated just as a basic array handle.
Additionally, you can use its constructors or the viskores::cont::make_ArrayHandleSOA() functions to build a viskores::cont::ArrayHandleSOA from basic viskores::cont::ArrayHandle’s that hold the components.
-
template<typename ValueType>
ArrayHandleSOA<ValueType> viskores::cont::make_ArrayHandleSOA(std::initializer_list<viskores::cont::ArrayHandle<typename viskores::VecTraits<ValueType>::ComponentType, viskores::cont::StorageTagBasic>> &&componentArrays) Create a
viskores::cont::ArrayHandleSOAwith an initializer list of array handles.viskores::cont::ArrayHandle<T> components1; viskores::cont::ArrayHandle<T> components2; viskores::cont::ArrayHandle<T> components3; // Fill arrays... auto vecarray = viskores::cont::make_ArrayHandleSOA<viskores::Vec<T, 3>>( { components1, components2, components3 });
-
template<typename ComponentType, typename ...RemainingArrays>
ArrayHandleSOA<viskores::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingArrays...>::value>> viskores::cont::make_ArrayHandleSOA(const viskores::cont::ArrayHandle<ComponentType, viskores::cont::StorageTagBasic> &componentArray0, const RemainingArrays&... componentArrays) Create a
viskores::cont::ArrayHandleSOAwith a number of array handles.This only works if all the templated arguments are of type
viskores::cont::ArrayHandle<ComponentType>.viskores::cont::ArrayHandle<T> components1; viskores::cont::ArrayHandle<T> components2; viskores::cont::ArrayHandle<T> components3; // Fill arrays... auto vecarray = viskores::cont::make_ArrayHandleSOA(components1, components2, components3);
-
template<typename ValueType>
ArrayHandleSOA<ValueType> viskores::cont::make_ArrayHandleSOA(std::initializer_list<std::vector<typename viskores::VecTraits<ValueType>::ComponentType>> &&componentVectors) Create a
viskores::cont::ArrayHandleSOAwith an initializer list ofstd::vector.The data is copied from the
std::vectors to the array handle.std::vector<T> components1; std::vector<T> components2; std::vector<T> components3; // Fill arrays... auto vecarray = viskores::cont::make_ArrayHandleSOA<viskores::Vec<T, 3>>( { components1, components2, components3 });
-
template<typename ComponentType, typename ...RemainingVectors>
ArrayHandleSOA<viskores::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>> viskores::cont::make_ArrayHandleSOA(viskores::CopyFlag copy, const std::vector<ComponentType> &vector0, RemainingVectors&&... componentVectors) Create a
viskores::cont::ArrayHandleSOAwith a number ofstd::vector.The first argument is a
viskores::CopyFlagto determine whether the input arrays should be copied. The component arrays are listed as arguments. This only works if all the templated arguments are of typestd::vector<ComponentType>.std::vector<T> components1; std::vector<T> components2; std::vector<T> components3; // Fill arrays... auto vecarray = viskores::cont::make_ArrayHandleSOA( viskores::CopyFlag::On, components1, components2, components3);
-
template<typename ComponentType, typename ...RemainingVectors>
ArrayHandleSOA<viskores::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>> viskores::cont::make_ArrayHandleSOA(viskores::CopyFlag copy, std::vector<ComponentType> &&vector0, RemainingVectors&&... componentVectors) Create a
viskores::cont::ArrayHandleSOAwith a number ofstd::vector.The first argument is a
viskores::CopyFlagto determine whether the input arrays should be copied. The component arrays are listed as arguments. This only works if all the templated arguments are rvalues of typestd::vector<ComponentType>.std::vector<T> components1; std::vector<T> components2; std::vector<T> components3; // Fill arrays... auto vecarray = viskores::cont::make_ArrayHandleSOA(viskores::CopyFlag::Off, std::move(components1), std::move(components2), std::move(components3);
-
template<typename ComponentType, typename ...RemainingVectors>
ArrayHandleSOA<viskores::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>> viskores::cont::make_ArrayHandleSOAMove(std::vector<ComponentType> &&vector0, RemainingVectors&&... componentVectors) Create a
viskores::cont::ArrayHandleSOAwith a number ofstd::vector.This only works if all the templated arguments are rvalues of type
std::vector<ComponentType>.std::vector<T> components1; std::vector<T> components2; std::vector<T> components3; // Fill arrays... auto vecarray = viskores::cont::make_ArrayHandleSOAMove( std::move(components1), std::move(components2), std::move(components3));
-
template<typename ValueType>
ArrayHandleSOA<ValueType> viskores::cont::make_ArrayHandleSOA(std::initializer_list<const typename viskores::VecTraits<ValueType>::ComponentType*> &&componentVectors, viskores::Id length, viskores::CopyFlag copy) Create a
viskores::cont::ArrayHandleSOAwith an initializer list of C arrays.T* components1; T* components2; T* components3; // Fill arrays... auto vecarray = viskores::cont::make_ArrayHandleSOA<viskores::Vec<T, 3>>( { components1, components2, components3 }, size, viskores::CopyFlag::On);
-
template<typename ComponentType, typename ...RemainingArrays>
ArrayHandleSOA<viskores::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingArrays...>::value>> viskores::cont::make_ArrayHandleSOA(viskores::Id length, viskores::CopyFlag copy, const ComponentType *array0, const RemainingArrays*... componentArrays) Create a
viskores::cont::ArrayHandleSOAwith a number of C arrays.This only works if all the templated arguments are of type
ComponentType*.T* components1; T* components2; T* components3; // Fill arrays... auto vecarray = viskores::cont::make_ArrayHandleSOA( size, viskores::CopyFlag::On, components1, components2, components3);
1 viskores::cont::ArrayHandle<viskores::FloatDefault> component1;
2 viskores::cont::ArrayHandle<viskores::FloatDefault> component2;
3 viskores::cont::ArrayHandle<viskores::FloatDefault> component3;
4 // Fill component arrays...
5
6 viskores::cont::ArrayHandleSOA<viskores::Vec3f> soaArray =
7 viskores::cont::make_ArrayHandleSOA(component1, component2, component3);
Did You Know?
In addition to constructing a viskores::cont::ArrayHandleSOA from its component arrays, you can get the component arrays back out using the viskores::cont::ArrayHandleSOA::GetArray() method.
4.8.3. Strided Arrays
viskores::cont::ArrayHandleBasic operates on a tightly packed array.
That is, each value follows immediately after the proceeding value in memory.
However, it is often convenient to access values at different strides or offsets.
This allows representations of data that are not tightly packed in memory.
The viskores::cont::ArrayHandleStride class allows arrays with different data packing.
-
template<typename T>
class ArrayHandleStride : public viskores::cont::ArrayHandle<T, viskores::cont::StorageTagStride> An
ArrayHandlethat accesses a basic array with strides and offsets.ArrayHandleStrideis a simpleArrayHandlethat accesses data with a prescribed stride and offset. You specify the stride and offset at construction. So when a portal for thisArrayHandleGets orSets a value at a specific index, the value accessed in the underlying C array is:(index * stride) + offset
Optionally, you can also specify a modulo and divisor. If they are specified, the index mangling becomes:
(((index / divisor) % modulo) * stride) + offset
You can “disable” any of the aforementioned operations by setting them to the following values (most of which are arithmetic identities):
stride: 1
offset: 0
modulo: 0
divisor: 1
Note that all of these indices are referenced by the
ValueTypeof the array. So, anArrayHandleStride<viskores::Float32>with an offset of 1 will actually offset by 4 bytes (the size of aviskores::Float32).ArrayHandleStrideis used to provide a unified type for pulling a component out of anArrayHandle. This way, you can iterate over multiple components in an array without having to implement a template instance for each vector size or representation.Public Functions
-
inline ArrayHandleStride(const viskores::cont::ArrayHandle<T, viskores::cont::StorageTagBasic> &array, viskores::Id numValues, viskores::Id stride, viskores::Id offset, viskores::Id modulo = 0, viskores::Id divisor = 1)
Construct an
ArrayHandleStridefrom a basic array with specified access patterns.
-
inline viskores::Id GetStride() const
Get the stride that values are accessed.
The stride is the spacing between consecutive values. The stride is measured in terms of the number of values. A stride of 1 means a fully packed array. A stride of 2 means selecting every other values.
-
inline viskores::Id GetOffset() const
Get the offset to start reading values.
The offset is the number of values to skip before the first value. The offset is measured in terms of the number of values. An offset of 0 means the first value at the beginning of the array.
The offset is unaffected by the stride and dictates where the strides starts counting. For example, given an array with size 3 vectors packed into an array, a strided array referencing the middle component will have offset 1 and stride 3.
-
inline viskores::Id GetModulo() const
Get the modulus of the array index.
When the index is modulo a value, it becomes the remainder after dividing by that value. The effect of the modulus is to cause the index to repeat over the values in the array.
If the modulo is set to 0, then it is ignored.
-
inline viskores::Id GetDivisor() const
Get the divisor of the array index.
The index is divided by the divisor before the other effects. The default divisor of 1 will have no effect on the indexing. Setting the divisor to a value greater than 1 has the effect of repeating each value that many times.
-
inline viskores::cont::ArrayHandleBasic<T> GetBasicArray() const
Return the underlying data as a basic array handle.
It is common for the same basic array to be shared among multiple
viskores::cont::ArrayHandleStrideobjects.
The most common use of viskores::cont::ArrayHandleStride is to pull components out of arrays.
viskores::cont::ArrayHandleStride is seldom constructed directly.
Rather, Viskores has mechanisms to extract a component from an array.
To extract a component directly from a viskores::cont::ArrayHandle, use viskores::cont::ArrayExtractComponent().
-
template<typename T, typename S>
viskores::cont::ArrayHandleStride<typename viskores::VecTraits<T>::BaseComponentType> viskores::cont::ArrayExtractComponent(const viskores::cont::ArrayHandle<T, S> &src, viskores::IdComponent componentIndex, viskores::CopyFlag allowCopy = viskores::CopyFlag::On) Pulls a component out of an
ArrayHandle.Given an
ArrayHandleof any type,ArrayExtractComponentreturns anArrayHandleStrideof the base component type that contains the data for the specified array component. This function can be used to apply an operation on anArrayHandleone component at a time. Because the array type is alwaysArrayHandleStride, you can drastically cut down on the number of templates to instantiate (at a possible cost to performance).Note that
ArrayExtractComponentwill flatten out the indices of any vec value type and return anArrayExtractComponentof the base component type. For example, if you callArrayExtractComponenton anArrayHandlewith a value type ofviskores::Vec<viskores::Vec<viskores::Float32, 2>, 3>, you will get anArrayExtractComponent<viskores::Float32>returned. ThecomponentIndexprovided will be applied to the nested vector in depth first order. So in the previous example, acomponentIndexof 0 gets the values at [0][0],componentIndexof 1 gets [0][1],componentIndexof 2 gets [1][0], and so on.Some
ArrayHandles allow this method to return anArrayHandleStridethat shares the same memory as the the originalArrayHandle. This form will be used if possible. In this case, if data are written into theArrayHandleStride, they are also written into the originalArrayHandle. However, other forms will require copies into a new array. In this case, writes intoArrayHandleStridewill not affect the originalArrayHandle.For some operations, such as writing into an output array, this behavior of shared arrays is necessary. For this case, the optional argument
allowCopycan be set toviskores::CopyFlag::Offto prevent the copying behavior into the returnArrayHandleStride. If this is the case, anErrorBadValueis thrown. If the arrays can be shared, they always will be regardless of the value ofallowCopy.Many forms of
ArrayHandlehave optimized versions to pull out a component. Some, however, do not. In these cases, a fallback array copy, done in serial, will be performed. A warning will be logged to alert users of this likely performance bottleneck.As an implementation note, this function should not be overloaded directly. Instead,
ArrayHandleimplementations should provide a specialization ofviskores::cont::internal::ArrayExtractComponentImpl.
The main advantage of extracting components this way is to convert data represented in different types of arrays into an array of a single type.
For example, viskores::cont::ArrayHandleStride can represent a component from either a viskores::cont::ArrayHandleBasic or a viskores::cont::ArrayHandleSOA by just using different stride values.
This is used by viskores::cont::UnknownArrayHandle::ExtractComponent() and elsewhere to create a concrete array handle class without knowing the actual class.
Common Errors
Many, but not all, of Viskores’s arrays can be represented by a viskores::cont::ArrayHandleStride directly without copying.
If Viskores cannot easily create a viskores::cont::ArrayHandleStride when attempting such an operation, it will use a slow copying fallback.
A warning will be issued whenever this happens.
Be on the lookout for such warnings and consider changing the data representation when that happens.
4.8.4. Runtime Vec Arrays
Because many of the devices Viskores runs on cannot efficiently allocate memory while an algorithm is running, the data held in viskores::cont::ArrayHandle’s are usually required to be a static size.
For example, the viskores::Vec object often used as the value type for viskores::cont::ArrayHandle has a number of components that must be defined at compile time.
This is a problem in cases where the size of a vector object cannot be determined at compile time.
One class to help alleviate this problem is viskores::cont::ArrayHandleRuntimeVec.
This array handle stores data in the same way as viskores::cont::ArrayHandleBasic with a viskores::Vec value type, but the size of the Vec can be set at runtime.
-
template<typename ComponentType>
class ArrayHandleRuntimeVec : public viskores::cont::ArrayHandle<viskores::VecFromPortal<ArrayHandleBasic<ComponentType>::WritePortalType>, viskores::cont::StorageTagRuntimeVec> Fancy array handle for a basic array with runtime selected vec size.
It is sometimes the case that you need to create an array of
Vecs where the number of components is not known until runtime. This is problematic for normalArrayHandles because you have to specify the size of theVecs as a template parameter at compile time.ArrayHandleRuntimeVeccan be used in this case.Note that caution should be used with
ArrayHandleRuntimeVecbecause the size of theVecvalues is not known at compile time. Thus, the value type of this array is forced to a specialVecFromPortalclass that can cause surprises if treated as aVec. In particular, the staticNUM_COMPONENTSexpression does not exist. Furthermore, new variables of typeVecFromPortalcannot be created. This means that simple operators like+will not work because they require an intermediate object to be created. (Equal operators like+=do work because they are given an existing variable to place the output.)It is possible to provide an
ArrayHandleBasicof the same component type as the underlying storage for this array. In this case, the array will be accessed much in the same manner asArrayHandleGroupVec.ArrayHandleRuntimeVecalso allows you to convert the array to anArrayHandleBasicof the appropriateVectype (orcomponenttype). A runtime check will be performed to make sure the number of components matches.Public Functions
-
inline ArrayHandleRuntimeVec(viskores::IdComponent numComponents, const ComponentsArrayType &componentsArray = ComponentsArrayType{})
Construct an
ArrayHandleRuntimeVecwith a given number of components.- Parameters:
numComponents – The size of the
Vecs stored in the array. This must be specified at the time of construction.componentsArray – This optional parameter allows you to supply a basic array that holds the components. This provides a mechanism to group consecutive values into vectors.
-
inline viskores::IdComponent GetNumberOfComponents() const
Return the number of components in each vec value.
-
inline viskores::cont::ArrayHandleBasic<ComponentType> GetComponentsArray() const
Return a basic array containing the components stored in this array.
The returned array is shared with this object. Modifying the contents of one array will modify the other.
-
template<typename ValueType>
inline void AsArrayHandleBasic(viskores::cont::ArrayHandle<ValueType> &array) const Converts the array to that of a basic array handle.
This method converts the
ArrayHandleRuntimeVecto a simpleArrayHandleBasic. This is useful if theArrayHandleRuntimeVecis passed to a routine that works on an array of a specificVecsize (or scalars). After a runtime check, the array can be converted to a typical array and used as such.
-
template<typename ArrayType>
inline ArrayType AsArrayHandleBasic() const Converts the array to that of a basic array handle.
This method converts the
ArrayHandleRuntimeVecto a simpleArrayHandleBasic. This is useful if theArrayHandleRuntimeVecis passed to a routine that works on an array of a specificVecsize (or scalars). After a runtime check, the array can be converted to a typical array and used as such.
-
inline ArrayHandleRuntimeVec(viskores::IdComponent numComponents, const ComponentsArrayType &componentsArray = ComponentsArrayType{})
A viskores::cont::ArrayHandleRuntimeVec is easily created from existing data using one of the viskores::cont::make_ArrayHandleRuntimeVec() functions.
-
template<typename T>
auto viskores::cont::make_ArrayHandleRuntimeVec(viskores::IdComponent numComponents, const viskores::cont::ArrayHandle<T, viskores::cont::StorageTagBasic> &componentsArray = viskores::cont::ArrayHandle<T, viskores::cont::StorageTagBasic>{}) make_ArrayHandleRuntimeVecis convenience function to generate anArrayHandleRuntimeVec.It takes the number of components stored in each value’s
Vec, which must be specified on the construction of theArrayHandleRuntimeVec. If not specified, the number of components is set to 1.make_ArrayHandleRuntimeVeccan also optionally take an existing array of components, which will be grouped intoVecvalues based on the specified number of components.
-
template<typename T>
auto viskores::cont::make_ArrayHandleRuntimeVec(const viskores::cont::ArrayHandle<T, viskores::cont::StorageTagBasic> &componentsArray) Converts a basic array handle into an
ArrayHandleRuntimeVecwith 1 component.The constructed array is essentially equivalent but of a different type.
Viskores also provides several convenience functions to convert a basic C array or std::vector to a viskores::cont::ArrayHandleRuntimeVec.
-
template<typename T>
auto viskores::cont::make_ArrayHandleRuntimeVec(viskores::IdComponent numComponents, const T *array, viskores::Id numberOfValues, viskores::CopyFlag copy) A convenience function for creating an
ArrayHandleRuntimeVecfrom a standard C array.
-
template<typename T>
auto viskores::cont::make_ArrayHandleRuntimeVecMove(viskores::IdComponent numComponents, T *&array, viskores::Id numberOfValues, viskores::cont::internal::BufferInfo::Deleter deleter = internal::SimpleArrayDeleter<T>, viskores::cont::internal::BufferInfo::Reallocater reallocater = internal::SimpleArrayReallocater<T>) A convenience function to move a user-allocated array into an
ArrayHandleRuntimeVec.The provided array pointer will be reset to
nullptr. If the array was not allocated with thenew[]operator, then deleter and reallocater functions must be provided.
-
template<typename T, typename Allocator>
auto viskores::cont::make_ArrayHandleRuntimeVec(viskores::IdComponent numComponents, const std::vector<T, Allocator> &array, viskores::CopyFlag copy) A convenience function for creating an
ArrayHandleRuntimeVecfrom anstd::vector.
-
template<typename T, typename Allocator>
auto viskores::cont::make_ArrayHandleRuntimeVecMove(viskores::IdComponent numComponents, std::vector<T, Allocator> &&array) Move an
std::vectorinto anArrayHandleRuntimeVec.
The advantage of this class is that a viskores::cont::ArrayHandleRuntimeVec can be created in a routine that does not know the number of components at runtime and then later retrieved as a basic viskores::cont::ArrayHandle with a viskores::Vec of the correct size.
This often consists of a file reader or other data ingestion creating viskores::cont::ArrayHandleRuntimeVec objects and storing them in viskores::cont::UnknownArrayHandle, which is used as an array container for viskores::cont::DataSet.
Filters that then subsequently operate on the viskores::cont::DataSet can retrieve the data as a viskores::cont::ArrayHandle of the appropriate viskores::Vec size.
1void ReadArray(std::vector<float>& data, int& numComponents);
2
3viskores::cont::UnknownArrayHandle LoadData()
4{
5 // Read data from some external source where the vector size is determined at runtime.
6 std::vector<viskores::Float32> data;
7 int numComponents;
8 ReadArray(data, numComponents);
9
10 // Resulting ArrayHandleRuntimeVec gets wrapped in an UnknownArrayHandle
11 return viskores::cont::make_ArrayHandleRuntimeVecMove(
12 static_cast<viskores::IdComponent>(numComponents), std::move(data));
13}
14
15void UseVecArray(const viskores::cont::UnknownArrayHandle& array)
16{
17 using ExpectedArrayType = viskores::cont::ArrayHandle<viskores::Vec3f_32>;
18 if (!array.CanConvert<ExpectedArrayType>())
19 {
20 throw viskores::cont::ErrorBadType("Array unexpected type.");
21 }
22
23 ExpectedArrayType concreteArray = array.AsArrayHandle<ExpectedArrayType>();
24 // Do something with concreteArray...
25}
26
27void LoadAndRun()
28{
29 // Load data in a routine that does not know component size until runtime.
30 viskores::cont::UnknownArrayHandle array = LoadData();
31
32 // Use the data in a method that requires an array of static size.
33 // This will work as long as the `Vec` size matches correctly (3 in this case).
34 UseVecArray(array);
35}
Did You Know?
Wrapping a basic array in a viskores::cont::ArrayHandleRuntimeVec has a similar effect as wrapping the array in a viskores::cont::ArrayHandleGroupVec.
The difference is in the context in which they are used.
If the size of the Vec is known at compile time and the array is going to immediately be used (such as operated on by a worklet), then viskores::cont::ArrayHandleGroupVec should be used.
However, if the Vec size is not known or the array will be stored in an object like viskores::cont::UnknownArrayHandle, then viskores::cont::ArrayHandleRuntimeVec is a better choice.
It is also possible to get a viskores::cont::ArrayHandleRuntimeVec from a viskores::cont::UnknownArrayHandle that was originally stored as a basic array.
This is convenient for operations that want to operate on arrays with an unknown Vec size.
viskores::cont::ArrayHandleRuntimeVec to get an array regardless of the size of the contained viskores::Vec values. 1template<typename T>
2void WriteData(const T* data, std::size_t size, int numComponents);
3
4void WriteViskoresArray(const viskores::cont::UnknownArrayHandle& array)
5{
6 bool writeSuccess = false;
7 auto doWrite = [&](auto componentType)
8 {
9 using ComponentType = decltype(componentType);
10 using VecArrayType = viskores::cont::ArrayHandleRuntimeVec<ComponentType>;
11 if (array.CanConvert<VecArrayType>())
12 {
13 // Get the array as a runtime Vec.
14 VecArrayType runtimeVecArray = array.AsArrayHandle<VecArrayType>();
15
16 // Get the component array.
17 viskores::cont::ArrayHandleBasic<ComponentType> componentArray =
18 runtimeVecArray.GetComponentsArray();
19
20 // Use the general function to write the data.
21 WriteData(componentArray.GetReadPointer(),
22 componentArray.GetNumberOfValues(),
23 runtimeVecArray.GetNumberOfComponentsFlat());
24
25 writeSuccess = true;
26 }
27 };
28
29 // Figure out the base component type, retrieve the data (regardless
30 // of vec size), and write out the data.
31 viskores::ListForEach(doWrite, viskores::TypeListBaseC{});
32}
4.8.5. Recombined Vec Arrays of Strided Components
Viskores contains a special array, viskores::cont::ArrayHandleSOAStride, to combine component arrays represented in viskores::cont::ArrayHandleStride together to form Vec values.
viskores::cont::ArrayHandleSOAStride is similar to viskores::cont::ArrayHandleSOA (see Section 4.8.2 (Structure of Arrays)) except that it holds stride arrays for its components instead of basic arrays.
viskores::cont::ArrayHandleSOAStride is mainly provided for the implementation of extracting arrays out of a viskores::cont::UnknownArrayHandle (see Section 3.5.4.3 (Extracting a Known Value Type from Unknown Storage)).
-
template<typename T>
class ArrayHandleSOAStride : public viskores::cont::ArrayHandle<T, viskores::cont::StorageTagSOAStride> An
ArrayHandlethat stores each component in a separate physical array with striding.ArrayHandleSOAStridebehaves much like anArrayHandleSOAin that each component is (potentially) stored in a separate array. However, whereasArrayHandleSOAspecifically stores each component in a basic array,ArrayHandleSOAStriderepresents each component as anArrayHandleStride. This gives flexibility in the representation because the values do not have to be tightly packed. The spacing between values can be determined at runtime. This allowsArrayHandleSOAStrideto represent most memory array layouts. For example, although it behaves like an SOA array, it can point to an AOS array by having each component point to the same physical array with different offsets.ArrayHandleSOAStrideis also similar toArrayHandleRecombineVec. It can be used to represent unknown arrays by extracting each component. The difference is thatArrayHandleSOAStriderequires a fix sized value where the number of components is known at compile time. In contrast,ArrayHandleRecombineVeccan work with any size vector defined at runtime. However,ArrayHandleRecombineVecrequires a dynamically sizedVec-like object that has limited use. WhenArrayHandleSOAStridecan be used, it uses the same value type as the array it is mimicking.Public Types
-
using ComponentArrayType = viskores::cont::ArrayHandle<ComponentType, viskores::cont::StorageTagStride>
The type of each component array.
Public Functions
-
inline ArrayHandleSOAStride(const std::array<ComponentArrayType, NUM_COMPONENTS> &componentArrays)
Construct an
ArrayHandleSOAStridefrom a collection of component arrays.viskores::cont::ArrayHandleStride<T> components1; viskores::cont::ArrayHandleStride<T> components2; viskores::cont::ArrayHandleStride<T> components3; // Fill arrays... std::array<viskores::cont::ArrayHandleStride<T>, 3> allComponents{ components1, components2, components3 }; viskores::cont::ArrayHandleSOAStride<viskores::Vec<T, 3>> vecarray(allComponents);
-
inline ArrayHandleSOAStride(const std::vector<ComponentArrayType> &componentArrays)
Construct an
ArrayHandleSOAStridefrom a collection of component arrays.viskores::cont::ArrayHandleStride<T> components1; viskores::cont::ArrayHandleStride<T> components2; viskores::cont::ArrayHandleStride<T> components3; // Fill arrays... std::vector<viskores::cont::ArrayHandleStride<T>> allComponents{ components1, components2, components3 }; viskores::cont::ArrayHandleSOAStride<viskores::Vec<T, 3>> vecarray(allComponents);
-
inline ArrayHandleSOAStride(std::initializer_list<ComponentArrayType> &&componentArrays)
Construct an
ArrayHandleSOAStridefrom a collection of component arrays.viskores::cont::ArrayHandle<T> components1; viskores::cont::ArrayHandle<T> components2; viskores::cont::ArrayHandle<T> components3; // Fill arrays... viskores::cont::ArrayHandleSOAStride<viskores::Vec<T, 3> vecarray( { components1, components2, components3 });
-
inline ArrayHandleSOAStride(std::initializer_list<std::vector<ComponentType>> &&componentVectors)
Construct an
ArrayHandleSOAStridefrom a collection of component arrays.The data is copied from the
std::vectors to the array handle.std::vector<T> components1; std::vector<T> components2; std::vector<T> components3; // Fill arrays... viskores::cont::ArrayHandleSOAStride<viskores::Vec<T, 3>> vecarray( { components1, components2, components3 });
-
inline ComponentArrayType GetArray(viskores::IdComponent index) const
Get a basic array representing the component for the given index.
-
inline void SetArray(viskores::IdComponent index, const ComponentArrayType &array)
Replace a component array.
-
using ComponentArrayType = viskores::cont::ArrayHandle<ComponentType, viskores::cont::StorageTagStride>
Viskores also contains a similar special array names viskores::cont::ArrayHandleRecombineVec.
This array is similar to viskores::cont::ArrayHandleSOAStride except that
the number of components can be specified at runtime.
This is useful when you know little or nothing about the value type and storage type but comes with limitations in its use.
This class is likewise provided for extracting arrays out of a viskores::cont::UnknownArrayHandle (see Section 3.5.4.4 (Extracting An Unknown Amount of Components)).
-
template<typename ComponentType>
class ArrayHandleRecombineVec : public viskores::cont::ArrayHandle<internal::detail::RecombinedValueType<ComponentType>, viskores::cont::internal::StorageTagRecombineVec> A grouping of
ArrayHandleStrides into anArrayHandleofviskores::Vecs.The main intention of
ArrayHandleStrideis to pull out a component of anArrayHandlewithout knowing thereArrayHandle’s storage orviskores::Vecshape. However, usually you want to do an operation on all the components together.ArrayHandleRecombineVecimplements the functionality to easily take a group of extracted components and treat them as a singleArrayHandleofviskores::Vecvalues.Note that caution should be used with
ArrayHandleRecombineVecbecause the size of theviskores::Vecvalues is not known at compile time. Thus, the value type of this array is forced to a specialRecombineVecclass that can cause surprises if treated as aviskores::Vec. In particular, the staticNUM_COMPONENTSexpression does not exist. Furthermore, new variables of typeRecombineVeccannot be created. This means that simple operators like+will not work because they require an intermediate object to be created. (Equal operators like+=do work because they are given an existing variable to place the output.)Public Functions
-
inline viskores::IdComponent GetNumberOfComponents() const
Return the number of components in each value of the array.
This is also equal to the number of component arrays referenced by this fancy array.
ArrayHandleRecombineVecalways stores flat Vec values. As such, this number of components is the same as the number of base components.
-
inline viskores::cont::ArrayHandleStride<ComponentType> GetComponentArray(viskores::IdComponent componentIndex) const
Get the array storing the values for a particular component.
The returned array is a
viskores::cont::ArrayHandleStride. It is possible that the returned arrays from different components reference the same area of physical memory (usually referencing values interleaved with each other).
-
inline void AppendComponentArray(const viskores::cont::ArrayHandle<ComponentType, viskores::cont::StorageTagStride> &array)
Add a component array.
AppendComponentArray()provides an easy way to build anArrayHandleRecombineVecby iteratively adding the component arrays.
-
inline viskores::IdComponent GetNumberOfComponents() const