Add ArrayGetValue[s] to address issue #355.
This commit is contained in:
parent
cdd7c8993b
commit
399f23963e
42
docs/changelog/array-get-values.md
Normal file
42
docs/changelog/array-get-values.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Add ArrayGetValues to retrieve a subset of ArrayHandle values from a device.
|
||||
|
||||
An algorithm will often want to pull just a single value (or small subset of
|
||||
values) back from a device to check the results of a computation. Previously,
|
||||
there was no easy way to do this, and algorithm developers would often
|
||||
transfer vast quantities of data back to the host just to check a single value.
|
||||
|
||||
The new `vtkm::cont::ArrayGetValue` and `vtkm::cont::ArrayGetValues` functions
|
||||
simplify this operations and provide a method to just retrieve a portion of an
|
||||
array.
|
||||
|
||||
This utility provides several convenient overloads:
|
||||
|
||||
A single id may be passed into ArrayGetValue, or multiple ids may be specified
|
||||
to ArrayGetValues as an ArrayHandle<vtkm::Id>, a std::vector<vtkm::Id>, a
|
||||
c-array (pointer and size), or as a brace-enclosed initializer list.
|
||||
|
||||
The single result from ArrayGetValue may be returned or written to an output
|
||||
argument. Multiple results from ArrayGetValues may be returned as an
|
||||
std::vector<T>, or written to an output argument as an ArrayHandle<T> or a
|
||||
std::vector<T>.
|
||||
|
||||
Examples:
|
||||
|
||||
```
|
||||
vtkm::cont::ArrayHandle<T> data = ...;
|
||||
|
||||
// Fetch the first value in an array handle:
|
||||
T firstVal = vtkm::cont::ArrayGetValue(0, data);
|
||||
|
||||
// Fetch the first and third values in an array handle:
|
||||
std::vector<T> firstAndThird = vtkm::cont::ArrayGetValues({0, 2}, data);
|
||||
|
||||
// Fetch the first and last values in an array handle:
|
||||
std::vector<T> firstAndLast =
|
||||
vtkm::cont::ArrayGetValues({0, data.GetNumberOfValues() - 1}, data);
|
||||
|
||||
// Fetch the first 4 values into an array handle:
|
||||
const std::vector<vtkm::Id> ids{0, 1, 2, 3};
|
||||
vtkm::cont::ArrayHandle<T> firstFour;
|
||||
vtkm::cont::ArrayGetValues(ids, data, firstFour);
|
||||
```
|
249
vtkm/cont/ArrayGetValues.h
Normal file
249
vtkm/cont/ArrayGetValues.h
Normal file
@ -0,0 +1,249 @@
|
||||
//============================================================================
|
||||
// Copyright (c) Kitware, Inc.
|
||||
// All rights reserved.
|
||||
// See LICENSE.txt for details.
|
||||
//
|
||||
// This software is distributed WITHOUT ANY WARRANTY; without even
|
||||
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the above copyright notice for more information.
|
||||
//============================================================================
|
||||
#ifndef vtk_m_cont_ArrayGetValues_h
|
||||
#define vtk_m_cont_ArrayGetValues_h
|
||||
|
||||
#include <vtkm/cont/Algorithm.h>
|
||||
#include <vtkm/cont/ArrayHandle.h>
|
||||
#include <vtkm/cont/ArrayHandlePermutation.h>
|
||||
#include <vtkm/cont/ArrayPortalToIterators.h>
|
||||
#include <vtkm/cont/DeviceAdapterTag.h>
|
||||
#include <vtkm/cont/ErrorExecution.h>
|
||||
#include <vtkm/cont/Logging.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace cont
|
||||
{
|
||||
|
||||
/// \brief Obtain a small set of values from an ArrayHandle with minimal device
|
||||
/// transfers.
|
||||
///
|
||||
/// The values in @a data at the indices in @a ids are copied into a new array
|
||||
/// and returned. This is useful for retrieving a subset of an array from a
|
||||
/// device without transferring the entire array to the host.
|
||||
///
|
||||
/// These functions should not be called repeatedly in a loop to fetch all
|
||||
/// values from an array handle. The much more efficient way to do this is to
|
||||
/// use the proper control-side portals (ArrayHandle::GetPortalControl() and
|
||||
/// ArrayHandle::GetPortalConstControl()).
|
||||
///
|
||||
/// This method will attempt to copy the data using the device that the input
|
||||
/// data is already valid on. If the input data is only valid in the control
|
||||
/// environment or the device copy fails, a control-side copy is performed.
|
||||
///
|
||||
/// Since a serial control-side copy may be used, this method is only intended
|
||||
/// for copying small subsets of the input data. Larger subsets that would
|
||||
/// benefit from parallelization should prefer using the ArrayCopy method with
|
||||
/// an ArrayHandlePermutation.
|
||||
///
|
||||
/// This utility provides several convenient overloads:
|
||||
///
|
||||
/// A single id may be passed into ArrayGetValue, or multiple ids may be
|
||||
/// specified to ArrayGetValues as an ArrayHandle<vtkm::Id>, a
|
||||
/// std::vector<vtkm::Id>, a c-array (pointer and size), or as a brace-enclosed
|
||||
/// initializer list.
|
||||
///
|
||||
/// The single result from ArrayGetValue may be returned or written to an output
|
||||
/// argument. Multiple results from ArrayGetValues may be returned as an
|
||||
/// std::vector<T>, or written to an output argument as an ArrayHandle<T> or a
|
||||
/// std::vector<T>.
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// ```
|
||||
/// vtkm::cont::ArrayHandle<T> data = ...;
|
||||
///
|
||||
/// // Fetch the first value in an array handle:
|
||||
/// T firstVal = vtkm::cont::ArrayGetValue(0, data);
|
||||
///
|
||||
/// // Fetch the first and third values in an array handle:
|
||||
/// std::vector<T> firstAndThird = vtkm::cont::ArrayGetValues({0, 2}, data);
|
||||
///
|
||||
/// // Fetch the first and last values in an array handle:
|
||||
/// std::vector<T> firstAndLast =
|
||||
/// vtkm::cont::ArrayGetValues({0, data.GetNumberOfValues() - 1}, data);
|
||||
///
|
||||
/// // Fetch the first 4 values into an array handle:
|
||||
/// const std::vector<vtkm::Id> ids{0, 1, 2, 3};
|
||||
/// vtkm::cont::ArrayHandle<T> firstFour;
|
||||
/// vtkm::cont::ArrayGetValues(ids, data, firstFour);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
///@{
|
||||
///
|
||||
template <typename SIds, typename T, typename SData, typename SOut>
|
||||
VTKM_CONT void ArrayGetValues(const vtkm::cont::ArrayHandle<vtkm::Id, SIds>& ids,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data,
|
||||
vtkm::cont::ArrayHandle<T, SOut>& output)
|
||||
{
|
||||
bool copyComplete = false;
|
||||
|
||||
// Find the device that already has a copy of the data:
|
||||
vtkm::cont::DeviceAdapterId devId = data.GetDeviceAdapterId();
|
||||
|
||||
if (devId.GetValue() != VTKM_DEVICE_ADAPTER_UNDEFINED)
|
||||
{ // Data exists on some device -- use it:
|
||||
const auto input = vtkm::cont::make_ArrayHandlePermutation(ids, data);
|
||||
copyComplete = vtkm::cont::Algorithm::Copy(devId, input, output);
|
||||
if (!copyComplete)
|
||||
{ // Retry on any device if the first attempt failed.
|
||||
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
|
||||
"Failed to run ArrayGetValues on device '"
|
||||
<< devId.GetName()
|
||||
<< "'. Falling back to control-side copy.");
|
||||
copyComplete = vtkm::cont::Algorithm::Copy(vtkm::cont::DeviceAdapterTagAny{}, input, output);
|
||||
}
|
||||
}
|
||||
|
||||
if (!copyComplete)
|
||||
{ // Fallback to a control-side copy if the device copy fails or if the device
|
||||
// is undefined:
|
||||
const vtkm::Id numVals = ids.GetNumberOfValues();
|
||||
auto idPortal = ids.GetPortalConstControl();
|
||||
auto dataPortal = data.GetPortalConstControl();
|
||||
output.Allocate(numVals);
|
||||
auto outPortal = output.GetPortalControl();
|
||||
for (vtkm::Id i = 0; i < numVals; ++i)
|
||||
{
|
||||
outPortal.Set(i, dataPortal.Get(idPortal.Get(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename SIds, typename T, typename SData, typename Alloc>
|
||||
VTKM_CONT void ArrayGetValues(const vtkm::cont::ArrayHandle<vtkm::Id, SIds>& ids,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data,
|
||||
std::vector<T, Alloc>& output)
|
||||
{
|
||||
const std::size_t numVals = static_cast<std::size_t>(ids.GetNumberOfValues());
|
||||
|
||||
// Allocate the vector and toss its data pointer into the array handle.
|
||||
output.resize(numVals);
|
||||
auto result = vtkm::cont::make_ArrayHandle(output, vtkm::CopyFlag::Off);
|
||||
vtkm::cont::ArrayGetValues(ids, data, result);
|
||||
// Make sure to pull the data back to control before we dealloc the handle
|
||||
// that wraps the vec memory:
|
||||
result.SyncControlArray();
|
||||
}
|
||||
|
||||
template <typename SIds, typename T, typename SData>
|
||||
VTKM_CONT std::vector<T> ArrayGetValues(const vtkm::cont::ArrayHandle<vtkm::Id, SIds>& ids,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data)
|
||||
{
|
||||
std::vector<T> result;
|
||||
vtkm::cont::ArrayGetValues(ids, data, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc, typename SData, typename SOut>
|
||||
VTKM_CONT void ArrayGetValues(const std::vector<vtkm::Id, Alloc>& ids,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data,
|
||||
vtkm::cont::ArrayHandle<T, SOut>& output)
|
||||
{
|
||||
const auto idsAH = vtkm::cont::make_ArrayHandle(ids, vtkm::CopyFlag::Off);
|
||||
ArrayGetValues(idsAH, data, output);
|
||||
}
|
||||
|
||||
template <typename T, typename AllocId, typename SData, typename AllocOut>
|
||||
VTKM_CONT void ArrayGetValues(const std::vector<vtkm::Id, AllocId>& ids,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data,
|
||||
std::vector<T, AllocOut>& output)
|
||||
{
|
||||
const auto idsAH = vtkm::cont::make_ArrayHandle(ids, vtkm::CopyFlag::Off);
|
||||
ArrayGetValues(idsAH, data, output);
|
||||
}
|
||||
|
||||
template <typename T, typename Alloc, typename SData>
|
||||
VTKM_CONT std::vector<T> ArrayGetValues(const std::vector<vtkm::Id, Alloc>& ids,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data)
|
||||
{
|
||||
const auto idsAH = vtkm::cont::make_ArrayHandle(ids, vtkm::CopyFlag::Off);
|
||||
return ArrayGetValues(idsAH, data);
|
||||
}
|
||||
|
||||
template <typename T, typename SData, typename SOut>
|
||||
VTKM_CONT void ArrayGetValues(const std::initializer_list<vtkm::Id>& ids,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data,
|
||||
vtkm::cont::ArrayHandle<T, SOut>& output)
|
||||
{
|
||||
const auto idsAH = vtkm::cont::make_ArrayHandle(
|
||||
ids.begin(), static_cast<vtkm::Id>(ids.size()), vtkm::CopyFlag::Off);
|
||||
ArrayGetValues(idsAH, data, output);
|
||||
}
|
||||
|
||||
template <typename T, typename SData, typename Alloc>
|
||||
VTKM_CONT void ArrayGetValues(const std::initializer_list<vtkm::Id>& ids,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data,
|
||||
std::vector<T, Alloc>& output)
|
||||
{
|
||||
const auto idsAH = vtkm::cont::make_ArrayHandle(
|
||||
ids.begin(), static_cast<vtkm::Id>(ids.size()), vtkm::CopyFlag::Off);
|
||||
ArrayGetValues(idsAH, data, output);
|
||||
}
|
||||
template <typename T, typename SData>
|
||||
VTKM_CONT std::vector<T> ArrayGetValues(const std::initializer_list<vtkm::Id>& ids,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data)
|
||||
{
|
||||
const auto idsAH = vtkm::cont::make_ArrayHandle(
|
||||
ids.begin(), static_cast<vtkm::Id>(ids.size()), vtkm::CopyFlag::Off);
|
||||
return ArrayGetValues(idsAH, data);
|
||||
}
|
||||
|
||||
template <typename T, typename SData, typename SOut>
|
||||
VTKM_CONT void ArrayGetValues(const vtkm::Id* ids,
|
||||
const vtkm::Id numIds,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data,
|
||||
vtkm::cont::ArrayHandle<T, SOut>& output)
|
||||
{
|
||||
const auto idsAH = vtkm::cont::make_ArrayHandle(ids, numIds, vtkm::CopyFlag::Off);
|
||||
ArrayGetValues(idsAH, data, output);
|
||||
}
|
||||
|
||||
template <typename T, typename SData, typename Alloc>
|
||||
VTKM_CONT void ArrayGetValues(const vtkm::Id* ids,
|
||||
const vtkm::Id numIds,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data,
|
||||
std::vector<T, Alloc>& output)
|
||||
{
|
||||
const auto idsAH = vtkm::cont::make_ArrayHandle(ids, numIds, vtkm::CopyFlag::Off);
|
||||
ArrayGetValues(idsAH, data, output);
|
||||
}
|
||||
template <typename T, typename SData>
|
||||
VTKM_CONT std::vector<T> ArrayGetValues(const vtkm::Id* ids,
|
||||
const vtkm::Id numIds,
|
||||
const vtkm::cont::ArrayHandle<T, SData>& data)
|
||||
{
|
||||
const auto idsAH = vtkm::cont::make_ArrayHandle(ids, numIds, vtkm::CopyFlag::Off);
|
||||
return ArrayGetValues(idsAH, data);
|
||||
}
|
||||
|
||||
template <typename T, typename S>
|
||||
VTKM_CONT T ArrayGetValue(vtkm::Id id, const vtkm::cont::ArrayHandle<T, S>& data)
|
||||
{
|
||||
const auto idAH = vtkm::cont::make_ArrayHandle(&id, 1, vtkm::CopyFlag::Off);
|
||||
auto result = vtkm::cont::ArrayGetValues(idAH, data);
|
||||
return result[0];
|
||||
}
|
||||
|
||||
template <typename T, typename S>
|
||||
VTKM_CONT void ArrayGetValue(vtkm::Id id, const vtkm::cont::ArrayHandle<T, S>& data, T& val)
|
||||
{
|
||||
val = ArrayGetValue(id, data);
|
||||
}
|
||||
/// @}
|
||||
}
|
||||
} // namespace vtkm::cont
|
||||
|
||||
#endif //vtk_m_cont_ArrayGetValues_h
|
@ -11,6 +11,7 @@
|
||||
set(headers
|
||||
Algorithm.h
|
||||
ArrayCopy.h
|
||||
ArrayGetValues.h
|
||||
ArrayHandle.h
|
||||
ArrayHandleBitField.h
|
||||
ArrayHandleCartesianProduct.h
|
||||
|
@ -35,6 +35,7 @@ vtkm_declare_headers(${headers})
|
||||
set(unit_tests
|
||||
UnitTestAlgorithm.cxx
|
||||
UnitTestArrayCopy.cxx
|
||||
UnitTestArrayGetValues.cxx
|
||||
UnitTestArrayHandleCartesianProduct.cxx
|
||||
UnitTestArrayHandleCompositeVector.cxx
|
||||
UnitTestArrayHandleCounting.cxx
|
||||
|
155
vtkm/cont/testing/UnitTestArrayGetValues.cxx
Normal file
155
vtkm/cont/testing/UnitTestArrayGetValues.cxx
Normal file
@ -0,0 +1,155 @@
|
||||
//============================================================================
|
||||
// Copyright (c) Kitware, Inc.
|
||||
// All rights reserved.
|
||||
// See LICENSE.txt for details.
|
||||
//
|
||||
// This software is distributed WITHOUT ANY WARRANTY; without even
|
||||
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
// PURPOSE. See the above copyright notice for more information.
|
||||
//============================================================================
|
||||
|
||||
#include <vtkm/cont/ArrayCopy.h>
|
||||
#include <vtkm/cont/ArrayGetValues.h>
|
||||
#include <vtkm/cont/ArrayHandleIndex.h>
|
||||
|
||||
#include <vtkm/cont/testing/Testing.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static constexpr vtkm::Id ARRAY_SIZE = 10;
|
||||
|
||||
template <typename T>
|
||||
VTKM_CONT void TestValues(const vtkm::cont::ArrayHandle<T>& ah,
|
||||
const std::initializer_list<T>& expected)
|
||||
{
|
||||
auto portal = ah.GetPortalConstControl();
|
||||
VTKM_TEST_ASSERT(expected.size() == static_cast<size_t>(ah.GetNumberOfValues()));
|
||||
for (vtkm::Id i = 0; i < ah.GetNumberOfValues(); ++i)
|
||||
{
|
||||
VTKM_TEST_ASSERT(expected.begin()[static_cast<size_t>(i)] == portal.Get(i));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
VTKM_CONT void TestValues(const std::vector<T>& vec, const std::initializer_list<T>& expected)
|
||||
{
|
||||
VTKM_TEST_ASSERT(expected.size() == vec.size());
|
||||
for (std::size_t i = 0; i < vec.size(); ++i)
|
||||
{
|
||||
VTKM_TEST_ASSERT(expected.begin()[static_cast<size_t>(i)] == vec[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
void TryCopy()
|
||||
{
|
||||
std::cout << "Trying type: " << vtkm::testing::TypeName<ValueType>::Name() << std::endl;
|
||||
|
||||
vtkm::cont::ArrayHandle<ValueType> data;
|
||||
{ // Create the ValueType array.
|
||||
vtkm::cont::ArrayHandleIndex values(ARRAY_SIZE);
|
||||
vtkm::cont::ArrayCopy(values, data);
|
||||
}
|
||||
|
||||
{ // ArrayHandle ids
|
||||
const std::vector<vtkm::Id> idsVec{ 3, 8, 7 };
|
||||
const auto ids = vtkm::cont::make_ArrayHandle(idsVec);
|
||||
{ // Return vector:
|
||||
const std::vector<ValueType> output = vtkm::cont::ArrayGetValues(ids, data);
|
||||
TestValues<ValueType>(output, { 3, 8, 7 });
|
||||
}
|
||||
{ // Pass vector:
|
||||
std::vector<ValueType> output;
|
||||
vtkm::cont::ArrayGetValues(ids, data, output);
|
||||
TestValues<ValueType>(output, { 3, 8, 7 });
|
||||
}
|
||||
{ // Pass handle:
|
||||
vtkm::cont::ArrayHandle<ValueType> output;
|
||||
vtkm::cont::ArrayGetValues(ids, data, output);
|
||||
TestValues<ValueType>(output, { 3, 8, 7 });
|
||||
}
|
||||
}
|
||||
|
||||
{ // vector ids
|
||||
const std::vector<vtkm::Id> ids{ 1, 5, 3, 9 };
|
||||
{ // Return vector:
|
||||
const std::vector<ValueType> output = vtkm::cont::ArrayGetValues(ids, data);
|
||||
TestValues<ValueType>(output, { 1, 5, 3, 9 });
|
||||
}
|
||||
{ // Pass vector:
|
||||
std::vector<ValueType> output;
|
||||
vtkm::cont::ArrayGetValues(ids, data, output);
|
||||
TestValues<ValueType>(output, { 1, 5, 3, 9 });
|
||||
}
|
||||
{ // Pass handle:
|
||||
vtkm::cont::ArrayHandle<ValueType> output;
|
||||
vtkm::cont::ArrayGetValues(ids, data, output);
|
||||
TestValues<ValueType>(output, { 1, 5, 3, 9 });
|
||||
}
|
||||
}
|
||||
|
||||
{ // Initializer list ids
|
||||
{ // Return vector:
|
||||
const std::vector<ValueType> output = vtkm::cont::ArrayGetValues({ 4, 2, 0, 6, 9 }, data);
|
||||
TestValues<ValueType>(output, { 4, 2, 0, 6, 9 });
|
||||
}
|
||||
{ // Pass vector:
|
||||
std::vector<ValueType> output;
|
||||
vtkm::cont::ArrayGetValues({ 4, 2, 0, 6, 9 }, data, output);
|
||||
TestValues<ValueType>(output, { 4, 2, 0, 6, 9 });
|
||||
}
|
||||
{ // Pass handle:
|
||||
vtkm::cont::ArrayHandle<ValueType> output;
|
||||
vtkm::cont::ArrayGetValues({ 4, 2, 0, 6, 9 }, data, output);
|
||||
TestValues<ValueType>(output, { 4, 2, 0, 6, 9 });
|
||||
}
|
||||
}
|
||||
|
||||
{ // c-array ids
|
||||
const std::vector<vtkm::Id> idVec{ 8, 6, 7, 5, 3, 0, 9 };
|
||||
const vtkm::Id* ids = idVec.data();
|
||||
const vtkm::Id n = static_cast<vtkm::Id>(idVec.size());
|
||||
{ // Return vector:
|
||||
const std::vector<ValueType> output = vtkm::cont::ArrayGetValues(ids, n, data);
|
||||
TestValues<ValueType>(output, { 8, 6, 7, 5, 3, 0, 9 });
|
||||
}
|
||||
{ // Pass vector:
|
||||
std::vector<ValueType> output;
|
||||
vtkm::cont::ArrayGetValues(ids, n, data, output);
|
||||
TestValues<ValueType>(output, { 8, 6, 7, 5, 3, 0, 9 });
|
||||
}
|
||||
{ // Pass handle:
|
||||
vtkm::cont::ArrayHandle<ValueType> output;
|
||||
vtkm::cont::ArrayGetValues(ids, n, data, output);
|
||||
TestValues<ValueType>(output, { 8, 6, 7, 5, 3, 0, 9 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{ // single values
|
||||
{
|
||||
const ValueType output = vtkm::cont::ArrayGetValue(8, data);
|
||||
VTKM_TEST_ASSERT(output == static_cast<ValueType>(8));
|
||||
}
|
||||
{
|
||||
ValueType output;
|
||||
vtkm::cont::ArrayGetValue(8, data, output);
|
||||
VTKM_TEST_ASSERT(output == static_cast<ValueType>(8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Test()
|
||||
{
|
||||
TryCopy<vtkm::Id>();
|
||||
TryCopy<vtkm::IdComponent>();
|
||||
TryCopy<vtkm::Float32>();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
int UnitTestArrayGetValues(int argc, char* argv[])
|
||||
{
|
||||
return vtkm::cont::testing::Testing::Run(Test, argc, argv);
|
||||
}
|
Loading…
Reference in New Issue
Block a user