Add AtomicArrayInOut ControlSignature tag.

This makes it easier to pass arrays with atomics to a worklet.
This commit is contained in:
Kenneth Moreland 2016-03-24 17:12:17 -06:00
parent 807b818abd
commit 43ed18b0e1
9 changed files with 326 additions and 14 deletions

@ -557,7 +557,7 @@ public:
&& !this->Internals->ExecutionArrayValid)
{
throw vtkm::cont::ErrorControlBadValue(
"ArrayHandle has no data when PrepareForInput called.");
"ArrayHandle has no data when PrepareForInPlace called.");
}
this->PrepareForDevice(DeviceAdapterTag());
@ -737,12 +737,12 @@ printSummary_ArrayHandle(const vtkm::cont::ArrayHandle<T,StorageT> &array,
else
{
out<<array.GetPortalConstControl().Get(0)<<" ";
out<<array.GetPortalConstControl().Get(1)<<" ";
out<<array.GetPortalConstControl().Get(2);
out<<array.GetPortalConstControl().Get(1)<<" ";
out<<array.GetPortalConstControl().Get(2);
out<<" ... ";
out<<array.GetPortalConstControl().Get(sz-3)<<" ";
out<<array.GetPortalConstControl().Get(sz-2)<<" ";
out<<array.GetPortalConstControl().Get(sz-1);
out<<array.GetPortalConstControl().Get(sz-2)<<" ";
out<<array.GetPortalConstControl().Get(sz-1);
}
out<<"]";
}
@ -764,12 +764,12 @@ printSummary_ArrayHandle(const vtkm::cont::ArrayHandle<vtkm::UInt8,StorageT> &ar
else
{
out<<static_cast<int>(array.GetPortalConstControl().Get(0))<<" ";
out<<static_cast<int>(array.GetPortalConstControl().Get(1))<<" ";
out<<static_cast<int>(array.GetPortalConstControl().Get(2));
out<<static_cast<int>(array.GetPortalConstControl().Get(1))<<" ";
out<<static_cast<int>(array.GetPortalConstControl().Get(2));
out<<" ... ";
out<<static_cast<int>(array.GetPortalConstControl().Get(sz-3))<<" ";
out<<static_cast<int>(array.GetPortalConstControl().Get(sz-2))<<" ";
out<<static_cast<int>(array.GetPortalConstControl().Get(sz-1));
out<<static_cast<int>(array.GetPortalConstControl().Get(sz-2))<<" ";
out<<static_cast<int>(array.GetPortalConstControl().Get(sz-1));
}
out<<"]";
}

@ -26,6 +26,7 @@ set(headers
TransportTagArrayIn.h
TransportTagArrayInOut.h
TransportTagArrayOut.h
TransportTagAtomicArray.h
TransportTagExecObject.h
TransportTagTopologyIn.h
TransportTagWholeArrayIn.h
@ -33,6 +34,7 @@ set(headers
TransportTagWholeArrayOut.h
TypeCheck.h
TypeCheckTagArray.h
TypeCheckTagAtomicArray.h
TypeCheckTagExecObject.h
TypeCheckTagTopology.h
)

@ -0,0 +1,71 @@
//============================================================================
// 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.
//
// Copyright 2015 Sandia Corporation.
// Copyright 2015 UT-Battelle, LLC.
// Copyright 2015 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_cont_arg_TransportTagAtomicArray_h
#define vtk_m_cont_arg_TransportTagAtomicArray_h
#include <vtkm/Types.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/StorageBasic.h>
#include <vtkm/cont/arg/Transport.h>
#include <vtkm/exec/AtomicArray.h>
namespace vtkm {
namespace cont {
namespace arg {
/// \brief \c Transport tag for in-place arrays with atomic operations.
///
/// \c TransportTagAtomicArray is a tag used with the \c Transport class to
/// transport \c ArrayHandle objects for data that is both input and output
/// (that is, in place modification of array data). The array will be wrapped
/// in a vtkm::exec::AtomicArray class that provides atomic operations (like
/// add and compare/swap).
///
struct TransportTagAtomicArray { };
template<typename T, typename Device>
struct Transport<
vtkm::cont::arg::TransportTagAtomicArray,
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>,
Device>
{
typedef vtkm::exec::AtomicArray<T, Device> ExecObjectType;
VTKM_CONT_EXPORT
ExecObjectType operator()(
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic> array,
vtkm::Id) const
{
// Note: we ignore the size of the domain because the randomly accessed
// array might not have the same size depending on how the user is using
// the array.
return ExecObjectType(array);
}
};
}
}
} // namespace vtkm::cont::arg
#endif //vtk_m_cont_arg_TransportTagAtomicArray_h

@ -0,0 +1,65 @@
//============================================================================
// 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.
//
// Copyright 2016 Sandia Corporation.
// Copyright 2016 UT-Battelle, LLC.
// Copyright 2016 Los Alamos National Security.
//
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_cont_arg_TypeCheckTagAtomicArray_h
#define vtk_m_cont_arg_TypeCheckTagAtomicArray_h
#include <vtkm/cont/arg/TypeCheck.h>
#include <vtkm/ListTag.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/StorageBasic.h>
#include <vtkm/exec/AtomicArray.h>
namespace vtkm {
namespace cont {
namespace arg {
/// The atomic array type check passes for an \c ArrayHandle of a structure
/// that is valid for atomic access. There are many restrictions on the
/// type of data that can be used for an atomic array.
///
template<typename TypeList = vtkm::exec::AtomicArrayTypeListTag>
struct TypeCheckTagAtomicArray
{
VTKM_IS_LIST_TAG(TypeList);
};
template<typename TypeList, typename ArrayType>
struct TypeCheck<TypeCheckTagAtomicArray<TypeList>, ArrayType>
{
static const bool value = false;
};
template<typename T, typename TypeList>
struct TypeCheck<TypeCheckTagAtomicArray<TypeList>,
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic> >
{
static const bool value =
( vtkm::ListContains<TypeList,T>::value &&
vtkm::ListContains<vtkm::exec::AtomicArrayTypeListTag,T>::value );
};
}
}
} // namespace vtkm::cont::arg
#endif //vtk_m_cont_arg_TypeCheckTagAtomicArray_h

@ -18,6 +18,7 @@
// this software.
//============================================================================
#include <vtkm/cont/arg/TransportTagAtomicArray.h>
#include <vtkm/cont/arg/TransportTagWholeArrayIn.h>
#include <vtkm/cont/arg/TransportTagWholeArrayInOut.h>
#include <vtkm/cont/arg/TransportTagWholeArrayOut.h>
@ -26,6 +27,7 @@
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/StorageBasic.h>
#include <vtkm/cont/testing/Testing.h>
@ -89,6 +91,23 @@ struct TestInOutKernel : public vtkm::exec::FunctorBase
}
};
template<typename AtomicType>
struct TestAtomicKernel : public vtkm::exec::FunctorBase
{
VTKM_CONT_EXPORT
TestAtomicKernel(const AtomicType &atomicArray)
: AtomicArray(atomicArray) { }
AtomicType AtomicArray;
VTKM_EXEC_EXPORT
void operator()(vtkm::Id index) const
{
typedef typename AtomicType::ValueType ValueType;
this->AtomicArray.Add(0, static_cast<ValueType>(index));
}
};
template<typename Device>
struct TryWholeArrayType
{
@ -142,11 +161,42 @@ struct TryWholeArrayType
}
};
template<typename Device>
struct TryAtomicArrayType
{
template<typename T>
void operator()(T) const
{
typedef vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>
ArrayHandleType;
typedef vtkm::cont::arg::Transport<
vtkm::cont::arg::TransportTagAtomicArray, ArrayHandleType, Device>
TransportType;
ArrayHandleType array;
array.Allocate(1);
array.GetPortalControl().Set(0, 0);
std::cout << "Check Transport AtomicArray" << std::endl;
TestAtomicKernel<typename TransportType::ExecObjectType>
kernel(TransportType()(array, -1));
vtkm::cont::DeviceAdapterAlgorithm<Device>::Schedule(kernel, ARRAY_SIZE);
T result = array.GetPortalConstControl().Get(0);
VTKM_TEST_ASSERT(result == ((ARRAY_SIZE-1)*ARRAY_SIZE)/2,
"Got wrong summation in atomic array.");
}
};
template<typename Device>
void TryArrayOutTransport(Device)
{
vtkm::testing::Testing::TryTypes(TryWholeArrayType<Device>(),
vtkm::TypeListTagCommon());
vtkm::testing::Testing::TryTypes(TryAtomicArrayType<Device>(),
vtkm::exec::AtomicArrayTypeListTag());
}
void TestWholeArrayTransport()

@ -19,6 +19,7 @@
//============================================================================
#include <vtkm/cont/arg/TypeCheckTagArray.h>
#include <vtkm/cont/arg/TypeCheckTagAtomicArray.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCompositeVector.h>
@ -61,6 +62,41 @@ struct TryArraysOfType
}
};
void TestCheckAtomicArray()
{
std::cout << "Trying some arrays with atomic arrays." << std::endl;
using vtkm::cont::arg::TypeCheck;
using vtkm::cont::arg::TypeCheckTagAtomicArray;
typedef vtkm::cont::ArrayHandle<vtkm::Int32> Int32Array;
typedef vtkm::cont::ArrayHandle<vtkm::Int64> Int64Array;
typedef vtkm::cont::ArrayHandle<vtkm::Float32> FloatArray;
typedef TypeCheckTagAtomicArray<> DefaultTypeCheck;
VTKM_TEST_ASSERT((TypeCheck<DefaultTypeCheck, Int32Array>::value),
"Check for 32-bit int failed.");
VTKM_TEST_ASSERT((TypeCheck<DefaultTypeCheck, Int64Array>::value),
"Check for 64-bit int failed.");
VTKM_TEST_ASSERT(!(TypeCheck<DefaultTypeCheck, FloatArray>::value),
"Check for float failed.");
typedef TypeCheckTagAtomicArray<vtkm::TypeListTagAll> ExpandedTypeCheck;
VTKM_TEST_ASSERT((TypeCheck<ExpandedTypeCheck, Int32Array>::value),
"Check for 32-bit int failed.");
VTKM_TEST_ASSERT((TypeCheck<ExpandedTypeCheck, Int64Array>::value),
"Check for 64-bit int failed.");
VTKM_TEST_ASSERT(!(TypeCheck<ExpandedTypeCheck, FloatArray>::value),
"Check for float failed.");
typedef TypeCheckTagAtomicArray<vtkm::ListTagBase<vtkm::Int32> > RestrictedTypeCheck;
VTKM_TEST_ASSERT((TypeCheck<RestrictedTypeCheck, Int32Array>::value),
"Check for 32-bit int failed.");
VTKM_TEST_ASSERT(!(TypeCheck<RestrictedTypeCheck, Int64Array>::value),
"Check for 64-bit int failed.");
VTKM_TEST_ASSERT(!(TypeCheck<RestrictedTypeCheck, FloatArray>::value),
"Check for float failed.");
}
void TestCheckArray()
{
vtkm::testing::Testing::TryAllTypes(TryArraysOfType());
@ -85,6 +121,8 @@ void TestCheckArray()
VTKM_TEST_ASSERT(
!(TypeCheck<TypeCheckTagArray<vtkm::TypeListTagFieldScalar>,VecArray>::value),
"Vector for scalar check failed.");
TestCheckAtomicArray();
}
} // anonymous namespace

@ -20,6 +20,7 @@
#ifndef vtk_m_exec_AtomicArray_h
#define vtk_m_exec_AtomicArray_h
#include <vtkm/ListTag.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/exec/ExecutionObjectBase.h>
@ -27,6 +28,10 @@
namespace vtkm {
namespace exec {
/// \brief A type list containing types that can be used with an AtomicArray.
///
struct AtomicArrayTypeListTag : vtkm::ListTagBase<vtkm::Int32,vtkm::Int64> { };
/// A class that can be used to atomically operate on an array of values safely
/// across multiple instances of the same worklet. This is useful when you have
/// an algorithm that needs to accumulate values in parallel, but writing out a
@ -45,6 +50,13 @@ template<typename T, typename DeviceAdapterTag>
class AtomicArray : public vtkm::exec::ExecutionObjectBase
{
public:
typedef T ValueType;
VTKM_CONT_EXPORT
AtomicArray()
: AtomicImplementation(vtkm::cont::make_ArrayHandle((T*)NULL, 0))
{ }
template<typename StorageType>
VTKM_CONT_EXPORT
AtomicArray(vtkm::cont::ArrayHandle<T, StorageType> handle):

@ -31,11 +31,13 @@
#include <vtkm/exec/arg/WorkIndex.h>
#include <vtkm/cont/arg/ControlSignatureTagBase.h>
#include <vtkm/cont/arg/TransportTagAtomicArray.h>
#include <vtkm/cont/arg/TransportTagExecObject.h>
#include <vtkm/cont/arg/TransportTagWholeArrayIn.h>
#include <vtkm/cont/arg/TransportTagWholeArrayInOut.h>
#include <vtkm/cont/arg/TransportTagWholeArrayOut.h>
#include <vtkm/cont/arg/TypeCheckTagArray.h>
#include <vtkm/cont/arg/TypeCheckTagAtomicArray.h>
#include <vtkm/cont/arg/TypeCheckTagExecObject.h>
#include <vtkm/worklet/ScatterIdentity.h>
@ -256,6 +258,25 @@ public:
typedef vtkm::exec::arg::FetchTagExecObject FetchTag;
};
/// \c ControlSignature tag for whole input/output arrays.
///
/// The \c AtomicArrayInOut control signature tag specifies an \c ArrayHandle
/// passed to the \c Invoke operation of the dispatcher. This is converted to
/// a \c vtkm::exec::AtomicArray object and passed to the appropriate worklet
/// operator argument with one of the default args. The provided atomic
/// operations can be used to resolve concurrency hazards, but have the
/// potential to slow the program quite a bit.
///
/// The template operator specifies all the potential value types of the
/// array. The default value type is all types.
///
template<typename TypeList = AllTypes>
struct AtomicArrayInOut : vtkm::cont::arg::ControlSignatureTagBase {
typedef vtkm::cont::arg::TypeCheckTagAtomicArray<TypeList> TypeCheckTag;
typedef vtkm::cont::arg::TransportTagAtomicArray TransportTag;
typedef vtkm::exec::arg::FetchTagExecObject FetchTag;
};
/// \brief Creates a \c ThreadIndices object.
///
/// Worklet types can add additional indices by returning different object

@ -62,13 +62,31 @@ public:
}
};
class TestAtomicArrayWorklet : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn<>, AtomicArrayInOut<>);
typedef void ExecutionSignature(WorkIndex, _2);
typedef _1 InputDomain;
template<typename AtomicArrayType>
VTKM_EXEC_EXPORT
void operator()(const vtkm::Id &index,
const AtomicArrayType &atomicArray) const
{
typedef typename AtomicArrayType::ValueType ValueType;
atomicArray.Add(0, static_cast<ValueType>(index));
}
};
namespace map_whole_array {
static const vtkm::Id ARRAY_SIZE = 10;
template<typename WorkletType>
struct DoTestWorklet
struct DoTestWholeArrayWorklet
{
typedef TestWholeArrayWorklet WorkletType;
// This just demonstrates that the WholeArray tags support dynamic arrays.
VTKM_CONT_EXPORT
void CallWorklet(const vtkm::cont::DynamicArrayHandle &inArray,
@ -112,6 +130,38 @@ struct DoTestWorklet
}
};
struct DoTestAtomicArrayWorklet
{
typedef TestAtomicArrayWorklet WorkletType;
// This just demonstrates that the WholeArray tags support dynamic arrays.
VTKM_CONT_EXPORT
void CallWorklet(const vtkm::cont::DynamicArrayHandle &inOutArray) const
{
std::cout << "Create and run dispatcher." << std::endl;
vtkm::worklet::DispatcherMapField<WorkletType> dispatcher;
dispatcher.Invoke(vtkm::cont::ArrayHandleIndex(ARRAY_SIZE), inOutArray);
}
template<typename T>
VTKM_CONT_EXPORT
void operator()(T) const
{
std::cout << "Set up data." << std::endl;
T inOutValue = 0;
vtkm::cont::ArrayHandle<T> inOutHandle =
vtkm::cont::make_ArrayHandle(&inOutValue, 1);
this->CallWorklet(vtkm::cont::DynamicArrayHandle(inOutHandle));
std::cout << "Check result." << std::endl;
T result = inOutHandle.GetPortalConstControl().Get(0);
VTKM_TEST_ASSERT(result == ((ARRAY_SIZE+1)*ARRAY_SIZE)/2,
"Got wrong summation in atomic array.");
}
};
void TestWorkletMapFieldExecArg()
{
typedef vtkm::cont::DeviceAdapterTraits<
@ -120,9 +170,12 @@ void TestWorkletMapFieldExecArg()
<< DeviceAdapterTraits::GetName() << std::endl;
std::cout << "--- Worklet accepting all types." << std::endl;
vtkm::testing::Testing::TryTypes(
map_whole_array::DoTestWorklet< TestWholeArrayWorklet >(),
vtkm::TypeListTagCommon());
vtkm::testing::Testing::TryTypes(map_whole_array::DoTestWholeArrayWorklet(),
vtkm::TypeListTagCommon());
std::cout << "--- Worklet accepting atomics." << std::endl;
vtkm::testing::Testing::TryTypes(map_whole_array::DoTestAtomicArrayWorklet(),
vtkm::exec::AtomicArrayTypeListTag());
}