vtk-m/vtkm/cont/ArrayGetValues.h
Kenneth Moreland 34615a9858 Deprecate ArrayHandle::GetDeviceAdapterId
This method is a remenant of when `ArrayHandle` could only store data on
one device at a time. It is now capable of storing data on any number of
devices (as well as the host), so asking for "the" device no longer
makes sense. Thus, this method is deprecated in favor of
`ArrayHandle::IsOnDevice`.

This deprecation leads to fixing some older functionality that still
assumed data could only be on one device at a time.

Fixes #592.
2021-07-28 12:51:51 -06:00

268 lines
10 KiB
C++

//============================================================================
// 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
{
namespace detail
{
struct ArrayGetValuesFunctor
{
template <typename Device, typename IdsArray, typename DataArray, typename OutputArray>
VTKM_CONT bool operator()(Device,
const IdsArray& ids,
const DataArray& data,
OutputArray& output) const
{
// Only get data on a device the data are already on.
if (data.IsOnDevice(Device{}))
{
const auto input = vtkm::cont::make_ArrayHandlePermutation(ids, data);
vtkm::cont::DeviceAdapterAlgorithm<Device>::Copy(input, output);
return true;
}
else
{
return false;
}
}
};
} // namespace detail
/// \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::WritePortal() and
/// ArrayHandle::ReadPortal()).
///
/// 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;
// If the data are not already on the host, attempt to copy on the device.
if (!data.IsOnHost())
{
copyComplete = vtkm::cont::TryExecute(detail::ArrayGetValuesFunctor{}, ids, data, output);
}
if (!copyComplete)
{ // Fallback to a control-side copy if the device copy fails or if the device
// is undefined or if the data were already on the host. In this case, the
// best we can do is grab the portals and copy one at a time on the host with
// a for loop.
const vtkm::Id numVals = ids.GetNumberOfValues();
auto idPortal = ids.ReadPortal();
auto dataPortal = data.ReadPortal();
output.Allocate(numVals);
auto outPortal = output.WritePortal();
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