Commit ae8d2f1f authored by Allison Vacanti's avatar Allison Vacanti Committed by Kitware Robot

Merge topic 'serial_copy'

7b66dece Add equality operators that handle different handle types.
1653f20e Add missing typedef to portal.
6c2f22b5 Overcome narrowing warning on MSVC.
1018d981 Check for overlap in CopySubRange.
374321e0 Use std::copy in TBB copy routines.
825f351d Use std::copy in serial Copy implementation.
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Merge-request: !967
parents b396716f 7b66dece
......@@ -321,12 +321,25 @@ public:
{
return (this->Internals == rhs.Internals);
}
VTKM_CONT
bool operator!=(const ArrayHandle<ValueType, StorageTag>& rhs) const
{
return (this->Internals != rhs.Internals);
}
template <typename VT, typename ST>
VTKM_CONT bool operator==(const ArrayHandle<VT, ST>&) const
{
return false; // different valuetype and/or storage
}
template <typename VT, typename ST>
VTKM_CONT bool operator!=(const ArrayHandle<VT, ST>&) const
{
return true; // different valuetype and/or storage
}
/// Get the storage.
///
VTKM_CONT StorageType& GetStorage();
......
......@@ -38,7 +38,8 @@ class VTKM_ALWAYS_EXPORT ArrayPortalExtractComponent
public:
using VectorType = typename PortalType::ValueType;
using Traits = vtkm::VecTraits<VectorType>;
using ComponentType = typename Traits::ComponentType;
using ValueType = typename Traits::ComponentType;
static const vtkm::IdComponent COMPONENT = Component;
VTKM_EXEC_CONT
......@@ -64,13 +65,13 @@ public:
vtkm::Id GetNumberOfValues() const { return this->Portal.GetNumberOfValues(); }
VTKM_EXEC_CONT
ComponentType Get(vtkm::Id index) const
ValueType Get(vtkm::Id index) const
{
return Traits::GetComponent(this->Portal.Get(index), Component);
}
VTKM_EXEC_CONT
void Set(vtkm::Id index, const ComponentType& value) const
void Set(vtkm::Id index, const ValueType& value) const
{
VectorType vec = this->Portal.Get(index);
Traits::SetComponent(vec, Component, value);
......@@ -104,7 +105,7 @@ public:
ArrayPortalExtractComponent<typename ArrayHandleType::PortalControl, Component>;
using PortalConstType =
ArrayPortalExtractComponent<typename ArrayHandleType::PortalConstControl, Component>;
using ValueType = typename PortalType::ComponentType;
using ValueType = typename PortalType::ValueType;
VTKM_CONT
Storage()
......
......@@ -108,7 +108,8 @@ struct DeviceAdapterAlgorithm
/// fit the number of elements we desire.
///
/// \par Requirements:
/// \arg \c input must already be sorted
/// \arg If \c input and \c output share memory, the input and output ranges
/// must not overlap.
///
template <typename T, typename U, class CIn, class COut>
VTKM_CONT static bool CopySubRange(const vtkm::cont::ArrayHandle<T, CIn>& input,
......
......@@ -805,6 +805,16 @@ public:
vtkm::Id outputIndex = 0)
{
const vtkm::Id inSize = input.GetNumberOfValues();
// Check if the ranges overlap and fail if they do.
if (input == output && ((outputIndex >= inputStartIndex &&
outputIndex < inputStartIndex + numberOfElementsToCopy) ||
(inputStartIndex >= outputIndex &&
inputStartIndex < outputIndex + numberOfElementsToCopy)))
{
return false;
}
if (inputStartIndex < 0 || numberOfElementsToCopy < 0 || outputIndex < 0 ||
inputStartIndex >= inSize)
{ //invalid parameters
......
......@@ -162,8 +162,15 @@ public:
VTKM_CONT Thisclass& operator=(const Thisclass& src);
VTKM_CONT Thisclass& operator=(Thisclass&& src);
VTKM_CONT bool operator==(const Thisclass& rhs) const;
VTKM_CONT bool operator!=(const Thisclass& rhs) const;
template <typename VT, typename ST>
VTKM_CONT bool operator==(const ArrayHandle<VT, ST>&) const;
template <typename VT, typename ST>
VTKM_CONT bool operator!=(const ArrayHandle<VT, ST>&) const;
VTKM_CONT StorageType& GetStorage();
VTKM_CONT const StorageType& GetStorage() const;
VTKM_CONT PortalControl GetPortalControl();
......
......@@ -89,6 +89,20 @@ bool ArrayHandle<T, StorageTagBasic>::operator!=(const Thisclass& rhs) const
return this->Internals != rhs.Internals;
}
template <typename T>
template <typename VT, typename ST>
VTKM_CONT bool ArrayHandle<T, StorageTagBasic>::operator==(const ArrayHandle<VT, ST>&) const
{
return false; // different valuetype and/or storage
}
template <typename T>
template <typename VT, typename ST>
VTKM_CONT bool ArrayHandle<T, StorageTagBasic>::operator!=(const ArrayHandle<VT, ST>&) const
{
return true; // different valuetype and/or storage
}
template <typename T>
typename ArrayHandle<T, StorageTagBasic>::StorageType& ArrayHandle<T, StorageTagBasic>::GetStorage()
{
......
......@@ -189,6 +189,16 @@ public:
vtkm::Id outputIndex = 0)
{
const vtkm::Id inSize = input.GetNumberOfValues();
// Check if the ranges overlap and fail if they do.
if (input == output && ((outputIndex >= inputStartIndex &&
outputIndex < inputStartIndex + numberOfElementsToCopy) ||
(inputStartIndex >= outputIndex &&
inputStartIndex < outputIndex + numberOfElementsToCopy)))
{
return false;
}
if (inputStartIndex < 0 || numberOfElementsToCopy < 0 || outputIndex < 0 ||
inputStartIndex >= inSize)
{ //invalid parameters
......
......@@ -33,7 +33,9 @@
#include <vtkm/exec/serial/internal/TaskTiling.h>
#include <algorithm>
#include <iterator>
#include <numeric>
#include <type_traits>
namespace vtkm
{
......@@ -49,7 +51,47 @@ struct DeviceAdapterAlgorithm<vtkm::cont::DeviceAdapterTagSerial>
private:
using Device = vtkm::cont::DeviceAdapterTagSerial;
// MSVC likes complain about narrowing type conversions in std::copy and
// provides no reasonable way to disable the warning. As a work-around, this
// template calls std::copy if and only if the types match, otherwise falls
// back to a iterative casting approach. Since std::copy can only really
// optimize same-type copies, this shouldn't affect performance.
template <typename InIter, typename OutIter>
VTKM_EXEC static void DoCopy(InIter src, InIter srcEnd, OutIter dst, std::false_type)
{
using OutputType = typename std::iterator_traits<OutIter>::value_type;
while (src != srcEnd)
{
*dst = static_cast<OutputType>(*src);
++src;
++dst;
}
}
template <typename InIter, typename OutIter>
VTKM_EXEC static void DoCopy(InIter src, InIter srcEnd, OutIter dst, std::true_type)
{
std::copy(src, srcEnd, dst);
}
public:
template <typename T, typename U, class CIn, class COut>
VTKM_CONT static void Copy(const vtkm::cont::ArrayHandle<T, CIn>& input,
vtkm::cont::ArrayHandle<U, COut>& output)
{
const vtkm::Id inSize = input.GetNumberOfValues();
auto inputPortal = input.PrepareForInput(DeviceAdapterTagSerial());
auto outputPortal = output.PrepareForOutput(inSize, DeviceAdapterTagSerial());
using InputType = decltype(inputPortal.Get(0));
using OutputType = decltype(outputPortal.Get(0));
DoCopy(vtkm::cont::ArrayPortalToIteratorBegin(inputPortal),
vtkm::cont::ArrayPortalToIteratorEnd(inputPortal),
vtkm::cont::ArrayPortalToIteratorBegin(outputPortal),
std::is_same<InputType, OutputType>());
}
template <typename T, typename U, class CIn, class CStencil, class COut>
VTKM_CONT static void CopyIf(const vtkm::cont::ArrayHandle<T, CIn>& input,
const vtkm::cont::ArrayHandle<U, CStencil>& stencil,
......@@ -87,6 +129,70 @@ public:
output.Shrink(writePos);
}
template <typename T, typename U, class CIn, class COut>
VTKM_CONT static bool CopySubRange(const vtkm::cont::ArrayHandle<T, CIn>& input,
vtkm::Id inputStartIndex,
vtkm::Id numberOfElementsToCopy,
vtkm::cont::ArrayHandle<U, COut>& output,
vtkm::Id outputIndex = 0)
{
const vtkm::Id inSize = input.GetNumberOfValues();
// Check if the ranges overlap and fail if they do.
if (input == output && ((outputIndex >= inputStartIndex &&
outputIndex < inputStartIndex + numberOfElementsToCopy) ||
(inputStartIndex >= outputIndex &&
inputStartIndex < outputIndex + numberOfElementsToCopy)))
{
return false;
}
if (inputStartIndex < 0 || numberOfElementsToCopy < 0 || outputIndex < 0 ||
inputStartIndex >= inSize)
{ //invalid parameters
return false;
}
//determine if the numberOfElementsToCopy needs to be reduced
if (inSize < (inputStartIndex + numberOfElementsToCopy))
{ //adjust the size
numberOfElementsToCopy = (inSize - inputStartIndex);
}
const vtkm::Id outSize = output.GetNumberOfValues();
const vtkm::Id copyOutEnd = outputIndex + numberOfElementsToCopy;
if (outSize < copyOutEnd)
{ //output is not large enough
if (outSize == 0)
{ //since output has nothing, just need to allocate to correct length
output.Allocate(copyOutEnd);
}
else
{ //we currently have data in this array, so preserve it in the new
//resized array
vtkm::cont::ArrayHandle<U, COut> temp;
temp.Allocate(copyOutEnd);
CopySubRange(output, 0, outSize, temp);
output = temp;
}
}
auto inputPortal = input.PrepareForInput(DeviceAdapterTagSerial());
auto outputPortal = output.PrepareForInPlace(DeviceAdapterTagSerial());
auto inIter = vtkm::cont::ArrayPortalToIteratorBegin(inputPortal);
auto outIter = vtkm::cont::ArrayPortalToIteratorBegin(outputPortal);
using InputType = decltype(inputPortal.Get(0));
using OutputType = decltype(outputPortal.Get(0));
DoCopy(inIter + inputStartIndex,
inIter + inputStartIndex + numberOfElementsToCopy,
outIter + outputIndex,
std::is_same<InputType, OutputType>());
return true;
}
template <typename T, typename U, class CIn>
VTKM_CONT static U Reduce(const vtkm::cont::ArrayHandle<T, CIn>& input, U initialValue)
{
......
......@@ -45,6 +45,17 @@ struct DeviceAdapterAlgorithm<vtkm::cont::DeviceAdapterTagTBB>
vtkm::cont::DeviceAdapterTagTBB>
{
public:
template <typename T, typename U, class CIn, class COut>
VTKM_CONT static void Copy(const vtkm::cont::ArrayHandle<T, CIn>& input,
vtkm::cont::ArrayHandle<U, COut>& output)
{
const vtkm::Id inSize = input.GetNumberOfValues();
auto inputPortal = input.PrepareForInput(DeviceAdapterTagTBB());
auto outputPortal = output.PrepareForOutput(inSize, DeviceAdapterTagTBB());
tbb::CopyPortals(inputPortal, outputPortal, 0, 0, inSize);
}
template <typename T, typename U, class CIn, class CStencil, class COut>
VTKM_CONT static void CopyIf(const vtkm::cont::ArrayHandle<T, CIn>& input,
const vtkm::cont::ArrayHandle<U, CStencil>& stencil,
......@@ -70,6 +81,63 @@ public:
output.Shrink(outputSize);
}
template <typename T, typename U, class CIn, class COut>
VTKM_CONT static bool CopySubRange(const vtkm::cont::ArrayHandle<T, CIn>& input,
vtkm::Id inputStartIndex,
vtkm::Id numberOfElementsToCopy,
vtkm::cont::ArrayHandle<U, COut>& output,
vtkm::Id outputIndex = 0)
{
const vtkm::Id inSize = input.GetNumberOfValues();
// Check if the ranges overlap and fail if they do.
if (input == output && ((outputIndex >= inputStartIndex &&
outputIndex < inputStartIndex + numberOfElementsToCopy) ||
(inputStartIndex >= outputIndex &&
inputStartIndex < outputIndex + numberOfElementsToCopy)))
{
return false;
}
if (inputStartIndex < 0 || numberOfElementsToCopy < 0 || outputIndex < 0 ||
inputStartIndex >= inSize)
{ //invalid parameters
return false;
}
//determine if the numberOfElementsToCopy needs to be reduced
if (inSize < (inputStartIndex + numberOfElementsToCopy))
{ //adjust the size
numberOfElementsToCopy = (inSize - inputStartIndex);
}
const vtkm::Id outSize = output.GetNumberOfValues();
const vtkm::Id copyOutEnd = outputIndex + numberOfElementsToCopy;
if (outSize < copyOutEnd)
{ //output is not large enough
if (outSize == 0)
{ //since output has nothing, just need to allocate to correct length
output.Allocate(copyOutEnd);
}
else
{ //we currently have data in this array, so preserve it in the new
//resized array
vtkm::cont::ArrayHandle<U, COut> temp;
temp.Allocate(copyOutEnd);
CopySubRange(output, 0, outSize, temp);
output = temp;
}
}
auto inputPortal = input.PrepareForInput(DeviceAdapterTagTBB());
auto outputPortal = output.PrepareForInPlace(DeviceAdapterTagTBB());
tbb::CopyPortals(
inputPortal, outputPortal, inputStartIndex, outputIndex, numberOfElementsToCopy);
return true;
}
template <typename T, typename U, class CIn>
VTKM_CONT static U Reduce(const vtkm::cont::ArrayHandle<T, CIn>& input, U initialValue)
{
......
......@@ -28,7 +28,9 @@
#include <vtkm/exec/internal/ErrorMessageBuffer.h>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <type_traits>
VTKM_THIRDPARTY_PRE_INCLUDE
......@@ -88,6 +90,83 @@ using WrappedBinaryOperator = vtkm::cont::internal::WrappedBinaryOperator<Result
// into picking this size.
static const vtkm::Id TBB_GRAIN_SIZE = 1024;
template <typename InputPortalType, typename OutputPortalType>
struct CopyBody
{
InputPortalType InputPortal;
OutputPortalType OutputPortal;
vtkm::Id InputOffset;
vtkm::Id OutputOffset;
VTKM_EXEC_CONT
CopyBody(const InputPortalType& inPortal,
const OutputPortalType& outPortal,
vtkm::Id inOffset,
vtkm::Id outOffset)
: InputPortal(inPortal)
, OutputPortal(outPortal)
, InputOffset(inOffset)
, OutputOffset(outOffset)
{
}
// MSVC likes complain about narrowing type conversions in std::copy and
// provides no reasonable way to disable the warning. As a work-around, this
// template calls std::copy if and only if the types match, otherwise falls
// back to a iterative casting approach. Since std::copy can only really
// optimize same-type copies, this shouldn't affect performance.
template <typename InIter, typename OutIter>
VTKM_EXEC void DoCopy(InIter src, InIter srcEnd, OutIter dst, std::false_type) const
{
using OutputType = typename std::iterator_traits<OutIter>::value_type;
while (src != srcEnd)
{
*dst = static_cast<OutputType>(*src);
++src;
++dst;
}
}
template <typename InIter, typename OutIter>
VTKM_EXEC void DoCopy(InIter src, InIter srcEnd, OutIter dst, std::true_type) const
{
std::copy(src, srcEnd, dst);
}
VTKM_EXEC
void operator()(const ::tbb::blocked_range<vtkm::Id>& range) const
{
if (range.empty())
{
return;
}
auto inIter = vtkm::cont::ArrayPortalToIteratorBegin(this->InputPortal);
auto outIter = vtkm::cont::ArrayPortalToIteratorBegin(this->OutputPortal);
using InputType = typename InputPortalType::ValueType;
using OutputType = typename OutputPortalType::ValueType;
this->DoCopy(inIter + this->InputOffset + range.begin(),
inIter + this->InputOffset + range.end(),
outIter + this->OutputOffset + range.begin(),
std::is_same<InputType, OutputType>());
}
};
template <typename InputPortalType, typename OutputPortalType>
void CopyPortals(const InputPortalType& inPortal,
const OutputPortalType& outPortal,
vtkm::Id inOffset,
vtkm::Id outOffset,
vtkm::Id numValues)
{
using Kernel = CopyBody<InputPortalType, OutputPortalType>;
Kernel kernel(inPortal, outPortal, inOffset, outOffset);
::tbb::blocked_range<vtkm::Id> range(0, numValues, TBB_GRAIN_SIZE);
::tbb::parallel_for(range, kernel);
}
template <typename InputPortalType,
typename StencilPortalType,
typename OutputPortalType,
......
......@@ -26,6 +26,7 @@
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/cont/ArrayHandleExtractComponent.h>
#include <vtkm/cont/serial/DeviceAdapterSerial.h>
#include <vtkm/cont/testing/Testing.h>
......@@ -68,6 +69,18 @@ void CheckArray(const vtkm::cont::ArrayHandle<T>& handle)
}
}
// Use to get an arbitrarily different valuetype than T:
template <typename T>
struct OtherType
{
using Type = vtkm::Int32;
};
template <>
struct OtherType<vtkm::Int32>
{
using Type = vtkm::UInt8;
};
/// This class has a single static member, Run, that tests that all Fancy Array
/// Handles work with the given DeviceAdapter
///
......@@ -326,6 +339,67 @@ private:
}
};
struct VerifyEqualityOperators
{
template <typename T>
VTKM_CONT void operator()(T) const
{
std::cout << "Verify that shallow copied array handles compare equal:\n";
{
vtkm::cont::ArrayHandle<T> a1;
vtkm::cont::ArrayHandle<T> a2 = a1; // shallow copy
vtkm::cont::ArrayHandle<T> a3;
VTKM_TEST_ASSERT(a1 == a2, "Shallow copied array not equal.");
VTKM_TEST_ASSERT(!(a1 != a2), "Shallow copied array not equal.");
VTKM_TEST_ASSERT(a1 != a3, "Distinct arrays compared equal.");
VTKM_TEST_ASSERT(!(a1 == a3), "Distinct arrays compared equal.");
// Operations on a1 shouldn't affect equality
a1.Allocate(200);
VTKM_TEST_ASSERT(a1 == a2, "Shallow copied array not equal.");
VTKM_TEST_ASSERT(!(a1 != a2), "Shallow copied array not equal.");
a1.GetPortalConstControl();
VTKM_TEST_ASSERT(a1 == a2, "Shallow copied array not equal.");
VTKM_TEST_ASSERT(!(a1 != a2), "Shallow copied array not equal.");
a1.PrepareForInPlace(DeviceAdapterTagSerial());
VTKM_TEST_ASSERT(a1 == a2, "Shallow copied array not equal.");
VTKM_TEST_ASSERT(!(a1 != a2), "Shallow copied array not equal.");
}
std::cout << "Verify that handles with different storage types are not equal.\n";
{
vtkm::cont::ArrayHandle<T, StorageTagBasic> a1;
vtkm::cont::ArrayHandle<vtkm::Vec<T, 3>, StorageTagBasic> tmp;
auto a2 = vtkm::cont::make_ArrayHandleExtractComponent<1>(tmp);
VTKM_TEST_ASSERT(a1 != a2, "Arrays with different storage type compared equal.");
VTKM_TEST_ASSERT(!(a1 == a2), "Arrays with different storage type compared equal.");
}
std::cout << "Verify that handles with different value types are not equal.\n";
{
vtkm::cont::ArrayHandle<T, StorageTagBasic> a1;
vtkm::cont::ArrayHandle<typename OtherType<T>::Type, StorageTagBasic> a2;
VTKM_TEST_ASSERT(a1 != a2, "Arrays with different value type compared equal.");
VTKM_TEST_ASSERT(!(a1 == a2), "Arrays with different value type compared equal.");
}
std::cout << "Verify that handles with different storage and value types are not equal.\n";
{
vtkm::cont::ArrayHandle<T, StorageTagBasic> a1;
vtkm::cont::ArrayHandle<vtkm::Vec<typename OtherType<T>::Type, 3>, StorageTagBasic> tmp;
auto a2 = vtkm::cont::make_ArrayHandleExtractComponent<1>(tmp);
VTKM_TEST_ASSERT(a1 != a2, "Arrays with different storage and value type compared equal.");
VTKM_TEST_ASSERT(!(a1 == a2),
"Arrays with different storage and value type compared equal.");
}
}
};
struct TryArrayHandleType
{
void operator()() const
......@@ -333,6 +407,7 @@ private:
vtkm::testing::Testing::TryTypes(VerifyEmptyArrays());
vtkm::testing::Testing::TryTypes(VerifyUserAllocatedHandle());
vtkm::testing::Testing::TryTypes(VerifyVTKMAllocatedHandle());
vtkm::testing::Testing::TryTypes(VerifyEqualityOperators());
}
};
......
......@@ -1967,6 +1967,29 @@ private:
}
}
// 7. Test that overlapping ranges trigger a failure:
// 7.1 output starts inside input range:
{
const vtkm::Id inBegin = 100;
const vtkm::Id inEnd = 200;
const vtkm::Id outBegin = 150;
const vtkm::Id numVals = inEnd - inBegin;
bool result = Algorithm::CopySubRange(input, inBegin, numVals, input, outBegin);
VTKM_TEST_ASSERT(result == false, "Overlapping subrange did not fail.");
}
// 7.2 input starts inside output range
{
const vtkm::Id inBegin = 100;
const vtkm::Id inEnd = 200;
const vtkm::Id outBegin = 50;
const vtkm::Id numVals = inEnd - inBegin;
bool result = Algorithm::CopySubRange(input, inBegin, numVals, input, outBegin);
VTKM_TEST_ASSERT(result == false, "Overlapping subrange did not fail.");
}
{
vtkm::cont::ArrayHandle<T> output;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment