Commit 0bcd172d authored by Kenneth Moreland's avatar Kenneth Moreland
Browse files

Merge branch 'atomic-improvements' into 'master'

Add AtomicArrayInOut ControlSignature tag

This makes it easier to pass arrays with atomics to a worklet.

See merge request !382
parents 6989f151 43ed18b0
......@@ -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
)
......
//============================================================================
// 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
//============================================================================
// 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,23 +28,35 @@
namespace vtkm {
namespace exec {
/// 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 value per worklet might be memory prohibitive.
/// \brief A type list containing types that can be used with an AtomicArray.
///
/// To construct an AtomicArray you will need to pass in an vtkm::cont::ArrayHandle
/// that is used as the underlying storage for the 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
/// value per worklet might be memory prohibitive.
///
/// To construct an AtomicArray you will need to pass in an
/// vtkm::cont::ArrayHandle that is used as the underlying storage for the
/// AtomicArray
///
/// Supported Operations: add / compare and swap (CAS)
///
///
/// Supported Types: 32 / 64 bit signed integers
///
///
///
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):
......@@ -58,9 +71,9 @@ public:
}
//
// Compare and Swap is an atomic exchange operation. If the value at
// Compare and Swap is an atomic exchange operation. If the value at
// the index is equal to oldValue, then newValue is written to the index.
// The operation was successful if return value is eqaul to oldValue
// The operation was successful if return value is equal to oldValue
//
VTKM_EXEC_EXPORT
T CompareAndSwap(vtkm::Id index, const T& newValue, const T& oldValue) const
......@@ -76,4 +89,4 @@ private:
}
} // namespace vtkm::exec
#endif //vtk_m_exec_AtomicArray_h
\ No newline at end of file
#endif //vtk_m_exec_AtomicArray_h
......@@ -20,6 +20,7 @@
set(headers
Assert.h
AtomicArray.h
CellDerivative.h
CellInterpolate.h
ConnectivityExplicit.h
......
......@@ -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());
}
......
Supports Markdown
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