Better fallback for ArrayGetValue

To avoid having to use a device compiler every time you wish to use
`ArrayGetValue`, the actual implementation is compiled into the `vtkm_cont`
library. To allow this to work for all the templated versions of
`ArrayHandle`, the implementation uses the extract component features of
`UnknownArrayHandle`. This works for most common arrays, but not all
arrays.

For arrays that cannot be directly represented by an `ArrayHandleStride`,
the fallback is bad. The entire array has to be pulled to the host and then
copied serially to a basic array.

For `ArrayGetValue`, this is just silly. So, for arrays that cannot be
simply represented by `ArrayHandleStride`, make a fallback that just uses
`ReadPortal` to get the data. Often this is not the most efficient method,
but it is better than the current alternative.
This commit is contained in:
Kenneth Moreland 2022-01-03 10:08:18 -07:00
parent 385ea820be
commit 5b34e6f70b
4 changed files with 61 additions and 4 deletions

@ -0,0 +1,17 @@
# Better fallback for ArrayGetValue
To avoid having to use a device compiler every time you wish to use
`ArrayGetValue`, the actual implementation is compiled into the `vtkm_cont`
library. To allow this to work for all the templated versions of
`ArrayHandle`, the implementation uses the extract component features of
`UnknownArrayHandle`. This works for most common arrays, but not all
arrays.
For arrays that cannot be directly represented by an `ArrayHandleStride`,
the fallback is bad. The entire array has to be pulled to the host and then
copied serially to a basic array.
For `ArrayGetValue`, this is just silly. So, for arrays that cannot be
simply represented by `ArrayHandleStride`, make a fallback that just uses
`ReadPortal` to get the data. Often this is not the most efficient method,
but it is better than the current alternative.

@ -66,8 +66,14 @@ ArrayExtractComponentFallback(const vtkm::cont::ArrayHandle<T, S>& src,
return vtkm::cont::ArrayHandleStride<BaseComponentType>(dest, numValues, 1, 0);
}
// Used as a superclass for ArrayHandleComponentImpls that are inefficient (and should be
// avoided).
struct ArrayExtractComponentImplInefficient
{
};
template <typename S>
struct ArrayExtractComponentImpl
struct ArrayExtractComponentImpl : ArrayExtractComponentImplInefficient
{
template <typename T>
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> operator()(
@ -131,6 +137,13 @@ struct ArrayExtractComponentImpl<vtkm::cont::StorageTagBasic>
}
};
/// \brief Resolves to true if ArrayHandleComponent of the array handle would be inefficient.
///
template <typename ArrayHandleType>
using ArrayExtractComponentIsInefficient = typename std::is_base_of<
vtkm::cont::internal::ArrayExtractComponentImplInefficient,
vtkm::cont::internal::ArrayExtractComponentImpl<typename ArrayHandleType::StorageTag>>::type;
} // namespace internal
/// \brief Pulls a component out of an `ArrayHandle`.

@ -19,7 +19,8 @@
void vtkm::cont::internal::ArrayGetValuesImpl(const vtkm::cont::UnknownArrayHandle& ids,
const vtkm::cont::UnknownArrayHandle& data,
const vtkm::cont::UnknownArrayHandle& output)
const vtkm::cont::UnknownArrayHandle& output,
std::false_type)
{
auto idArray = ids.ExtractComponent<vtkm::Id>(0, vtkm::CopyFlag::On);
output.Allocate(ids.GetNumberOfValues());

@ -31,7 +31,30 @@ namespace internal
VTKM_CONT_EXPORT void ArrayGetValuesImpl(const vtkm::cont::UnknownArrayHandle& ids,
const vtkm::cont::UnknownArrayHandle& data,
const vtkm::cont::UnknownArrayHandle& output);
const vtkm::cont::UnknownArrayHandle& output,
std::false_type extractComponentInefficient);
template <typename IdsArrayHandle, typename DataArrayHandle, typename OutputArrayHandle>
void ArrayGetValuesImpl(const IdsArrayHandle& ids,
const DataArrayHandle& data,
const OutputArrayHandle& output,
std::true_type vtkmNotUsed(extractComponentInefficient))
{
// Fallback implementation. Using UnknownArrayHandle to extract the data would be more
// inefficient than simply getting the ReadPortal (which could potentially copy everything
// form device to host), so we do that here. The only other alternative would be to write
// a custom worklet, but that would require a device compiler, and we are avoiding that for
// this header.
vtkm::Id outputSize = ids.GetNumberOfValues();
output.Allocate(outputSize);
auto idsPortal = ids.ReadPortal();
auto dataPortal = data.ReadPortal();
auto outputPortal = output.WritePortal();
for (vtkm::Id index = 0; index < outputSize; ++index)
{
outputPortal.Set(index, dataPortal.Get(idsPortal.Get(index)));
}
}
} // namespace internal
@ -100,7 +123,10 @@ VTKM_CONT void ArrayGetValues(const vtkm::cont::ArrayHandle<vtkm::Id, SIds>& ids
VTKM_STATIC_ASSERT_MSG(
vtkm::HasVecTraits<T>::value,
"ArrayGetValues can only be used with arrays containing value types with VecTraits defined.");
internal::ArrayGetValuesImpl(ids, data, output);
using DataArrayHandle = vtkm::cont::ArrayHandle<T, SData>;
using InefficientExtract =
vtkm::cont::internal::ArrayExtractComponentIsInefficient<DataArrayHandle>;
internal::ArrayGetValuesImpl(ids, data, output, InefficientExtract{});
}
/// We need a specialization for `ArrayHandleCasts` to avoid runtime type missmatch errors inside