Merge topic 'improve_connectivity_worklet_perf'

973878b8b Improve the performance of the Image and Graph Connectivity algorithms

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Li-Ta Lo <ollie@lanl.gov>
Merge-request: !1873
This commit is contained in:
Robert Maynard 2019-10-02 15:41:48 +00:00 committed by Kitware Robot
commit 308552bd62
4 changed files with 95 additions and 53 deletions

@ -95,6 +95,33 @@ public:
static_cast<APIType>(value)));
}
/// \brief Peform an atomic store to memory while enforcing, at minimum, "release"
/// memory ordering.
/// \param index The index of the array element that will be added to.
/// \param value The value to write for the atomic store operation.
/// \warning Using something like:
/// ```
/// Set(index, Get(index)+N)
/// ```
/// Should not be done as it is not thread safe, instead you should use
/// the provided Add method instead.
///
VTKM_SUPPRESS_EXEC_WARNINGS
VTKM_EXEC
void Set(vtkm::Id index, const ValueType& value) const
{
// We only support 32/64 bit signed/unsigned ints, and AtomicInterface
// currently only provides API for unsigned types.
// We'll cast the signed types to unsigned to work around this.
// This is safe, since the only difference between signed/unsigned types
// is how overflow works, and signed overflow is already undefined. We also
// document that overflow is undefined for this operation.
using APIType = typename std::make_unsigned<ValueType>::type;
AtomicInterface::Store(reinterpret_cast<APIType*>(this->Data + index),
static_cast<APIType>(value));
}
/// \brief Perform an atomic CAS operation with sequentially consistent
/// memory ordering.
/// \param index The index of the array element that will be atomically

@ -11,6 +11,7 @@
#ifndef vtk_m_worklet_connectivity_graph_connectivity_h
#define vtk_m_worklet_connectivity_graph_connectivity_h
#include <vtkm/cont/Invoker.h>
#include <vtkm/worklet/connectivities/CellSetDualGraph.h>
#include <vtkm/worklet/connectivities/InnerJoin.h>
#include <vtkm/worklet/connectivities/UnionFind.h>
@ -26,10 +27,12 @@ namespace detail
class Graft : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature =
void(FieldIn index, FieldIn start, FieldIn degree, WholeArrayIn ids, WholeArrayInOut comp);
using ControlSignature = void(FieldIn start,
FieldIn degree,
WholeArrayIn ids,
WholeArrayInOut comp);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
using ExecutionSignature = void(WorkIndex, _1, _2, _3, _4);
using InputDomain = _1;
@ -64,29 +67,29 @@ public:
const InputPortalType& connectivityArray,
OutputPortalType& componentsOut) const
{
bool allStar = false;
bool everythingIsAStar = false;
vtkm::cont::ArrayHandle<vtkm::Id> components;
vtkm::cont::ArrayHandle<bool> isStar;
vtkm::cont::ArrayHandle<vtkm::Id> cellIds;
Algorithm::Copy(
vtkm::cont::ArrayHandleCounting<vtkm::Id>(0, 1, numIndicesArray.GetNumberOfValues()),
cellIds);
Algorithm::Copy(cellIds, components);
components);
//used as an atomic bool, so we use Int32 as it the
//smallest type that VTK-m supports as atomics
vtkm::cont::ArrayHandle<vtkm::Int32> allStars;
allStars.Allocate(1);
vtkm::cont::Invoker invoke;
do
{
vtkm::worklet::DispatcherMapField<detail::Graft> graftDispatcher;
graftDispatcher.Invoke(
cellIds, indexOffsetsArray, numIndicesArray, connectivityArray, components);
allStars.GetPortalControl().Set(0, 1); //reset the atomic state
invoke(detail::Graft{}, indexOffsetsArray, numIndicesArray, connectivityArray, components);
// Detection of allStar has to come before pointer jumping. Don't try to rearrange it.
vtkm::worklet::DispatcherMapField<IsStar> isStarDisp;
isStarDisp.Invoke(cellIds, components, isStar);
allStar = Algorithm::Reduce(isStar, true, vtkm::LogicalAnd());
vtkm::worklet::DispatcherMapField<PointerJumping> pointJumpingDispatcher;
pointJumpingDispatcher.Invoke(cellIds, components);
} while (!allStar);
// Detection of allStars has to come before pointer jumping. Don't try to rearrange it.
invoke(IsStar{}, components, allStars);
everythingIsAStar = (allStars.GetPortalControl().Get(0) == 1);
invoke(PointerJumping{}, components);
} while (!everythingIsAStar);
// renumber connected component to the range of [0, number of components).
vtkm::cont::ArrayHandle<vtkm::Id> uniqueComponents;
@ -94,6 +97,11 @@ public:
Algorithm::Sort(uniqueComponents);
Algorithm::Unique(uniqueComponents);
vtkm::cont::ArrayHandle<vtkm::Id> cellIds;
Algorithm::Copy(
vtkm::cont::ArrayHandleCounting<vtkm::Id>(0, 1, numIndicesArray.GetNumberOfValues()),
cellIds);
vtkm::cont::ArrayHandle<vtkm::Id> uniqueColor;
Algorithm::Copy(
vtkm::cont::ArrayHandleCounting<vtkm::Id>(0, 1, uniqueComponents.GetNumberOfValues()),

@ -11,12 +11,10 @@
#ifndef vtk_m_worklet_connectivity_ImageConnectivity_h
#define vtk_m_worklet_connectivity_ImageConnectivity_h
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/DispatcherPointNeighborhood.h>
#include <vtkm/cont/Invoker.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletPointNeighborhood.h>
#include <iomanip>
#include <vtkm/worklet/connectivities/InnerJoin.h>
#include <vtkm/worklet/connectivities/UnionFind.h>
@ -34,13 +32,12 @@ class ImageGraft : public vtkm::worklet::WorkletPointNeighborhood
{
public:
using ControlSignature = void(CellSetIn,
FieldIn index,
FieldInNeighborhood compIn,
FieldInNeighborhood color,
WholeArrayInOut compOut,
FieldOut changed);
AtomicArrayInOut changed);
using ExecutionSignature = _6(_2, _3, _4, _5);
using ExecutionSignature = void(WorkIndex, _2, _3, _4, _5);
template <typename Comp>
VTKM_EXEC vtkm::Id findRoot(Comp& comp, vtkm::Id index) const
@ -51,11 +48,12 @@ public:
}
// compOut is an alias of compIn such that we can update component labels
template <typename NeighborComp, typename NeighborColor, typename CompOut>
VTKM_EXEC bool operator()(const vtkm::Id index,
template <typename NeighborComp, typename NeighborColor, typename CompOut, typename AtomicInOut>
VTKM_EXEC void operator()(const vtkm::Id index,
const NeighborComp& neighborComp,
const NeighborColor& neighborColor,
CompOut& compOut) const
CompOut& compOut,
AtomicInOut& updated) const
{
vtkm::Id myComp = neighborComp.Get(0, 0, 0);
auto minComp = myComp;
@ -88,8 +86,11 @@ public:
else if (myRoot > newRoot)
compOut.Set(myRoot, newRoot);
// return if the labeling has changed.
return myComp != minComp;
// mark an update occurred if no other worklets have done so yet
if (myComp != minComp && updated.Get(0) == 0)
{
updated.Set(0, 1);
}
}
};
}
@ -110,23 +111,18 @@ public:
Algorithm::Copy(vtkm::cont::ArrayHandleCounting<vtkm::Id>(0, 1, pixels.GetNumberOfValues()),
components);
vtkm::cont::ArrayHandle<vtkm::Id> pixelIds;
Algorithm::Copy(vtkm::cont::ArrayHandleCounting<vtkm::Id>(0, 1, pixels.GetNumberOfValues()),
pixelIds);
vtkm::cont::ArrayHandle<bool> changed;
using DispatcherType = vtkm::worklet::DispatcherPointNeighborhood<detail::ImageGraft>;
//used as an atomic bool, so we use Int32 as it the
//smallest type that VTK-m supports as atomics
vtkm::cont::ArrayHandle<vtkm::Int32> updateRequired;
updateRequired.Allocate(1);
vtkm::cont::Invoker invoke;
do
{
DispatcherType dispatcher;
dispatcher.Invoke(input, pixelIds, components, pixels, components, changed);
vtkm::worklet::DispatcherMapField<PointerJumping> pointJumpingDispatcher;
pointJumpingDispatcher.Invoke(pixelIds, components);
} while (Algorithm::Reduce(changed, false, vtkm::LogicalOr()));
updateRequired.GetPortalControl().Set(0, 0); //reset the atomic state
invoke(detail::ImageGraft{}, input, components, pixels, components, updateRequired);
invoke(PointerJumping{}, components);
} while (updateRequired.GetPortalControl().Get(0) > 0);
// renumber connected component to the range of [0, number of components).
vtkm::cont::ArrayHandle<vtkm::Id> uniqueComponents;
@ -134,10 +130,15 @@ public:
Algorithm::Sort(uniqueComponents);
Algorithm::Unique(uniqueComponents);
vtkm::cont::ArrayHandle<vtkm::Id> pixelIds;
Algorithm::Copy(vtkm::cont::ArrayHandleCounting<vtkm::Id>(0, 1, pixels.GetNumberOfValues()),
pixelIds);
vtkm::cont::ArrayHandle<vtkm::Id> uniqueColor;
Algorithm::Copy(
vtkm::cont::ArrayHandleCounting<vtkm::Id>(0, 1, uniqueComponents.GetNumberOfValues()),
uniqueColor);
vtkm::cont::ArrayHandle<vtkm::Id> cellColors;
vtkm::cont::ArrayHandle<vtkm::Id> pixelIdsOut;
InnerJoin().Run(
@ -176,8 +177,8 @@ public:
vtkm::cont::CastAndCall(pixels, RunImpl(), input, componentsOut);
}
template <typename T, typename S, typename OutputPortalType>
void Run(const vtkm::cont::DynamicCellSet& input,
template <typename CellSetTag, typename T, typename S, typename OutputPortalType>
void Run(const vtkm::cont::DynamicCellSetBase<CellSetTag>& input,
const vtkm::cont::ArrayHandle<T, S>& pixels,
OutputPortalType& componentsOut) const
{

@ -23,8 +23,8 @@ namespace connectivity
class PointerJumping : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn index, WholeArrayInOut comp);
using ExecutionSignature = void(_1, _2);
using ControlSignature = void(WholeArrayInOut comp);
using ExecutionSignature = void(WorkIndex, _1);
using InputDomain = _1;
template <typename Comp>
@ -47,14 +47,20 @@ public:
class IsStar : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn index, WholeArrayIn comp, FieldOut);
using ExecutionSignature = _3(_1, _2);
using ControlSignature = void(WholeArrayIn comp, AtomicArrayInOut);
using ExecutionSignature = void(WorkIndex, _1, _2);
using InputDomain = _1;
template <typename InOutPortalType>
VTKM_EXEC bool operator()(vtkm::Id index, InOutPortalType& comp) const
template <typename InOutPortalType, typename AtomicInOut>
VTKM_EXEC void operator()(vtkm::Id index, InOutPortalType& comp, AtomicInOut& hasStar) const
{
return comp.Get(index) == comp.Get(comp.Get(index));
//hasStar emulates a LogicalAnd across all the values
//where we start with a value of 'true'|1.
const bool isAStar = (comp.Get(index) == comp.Get(comp.Get(index)));
if (!isAStar && hasStar.Get(0) == 1)
{
hasStar.Set(0, 0);
}
}
};