GhostCellClassify now more efficient as it uses WorkletPointNeighborhood

By using the dual of the cellset we can quickly compute the GhostCells
of structured data using WorkletPointNeighborhood boundary condition
object

Using a 1024x1024x512 test grid we see the following perf:

Master Serial : 5.658144 sec
This MR Serial: 0.519684 sec

Master OpenMP : 0.532256 sec
This MR OpenMP: 0.080604 sec
This commit is contained in:
Robert Maynard 2019-09-10 15:00:53 -04:00
parent 1a70c6f9ed
commit 8c3a8da99b
6 changed files with 66 additions and 90 deletions

@ -13,7 +13,7 @@
namespace vtkm namespace vtkm
{ {
enum struct CellClassification enum CellClassification : vtkm::UInt8
{ {
NORMAL = 0, //Valid cell NORMAL = 0, //Valid cell
GHOST = 1 << 0, //Ghost cell GHOST = 1 << 0, //Ghost cell

@ -25,6 +25,8 @@ struct GhostCellClassifyPolicy : vtkm::filter::PolicyBase<GhostCellClassifyPolic
class GhostCellClassify : public vtkm::filter::FilterDataSet<GhostCellClassify> class GhostCellClassify : public vtkm::filter::FilterDataSet<GhostCellClassify>
{ {
public: public:
using SupportedTypes = vtkm::ListTagBase<vtkm::UInt8>;
VTKM_CONT VTKM_CONT
GhostCellClassify(); GhostCellClassify();

@ -15,9 +15,8 @@
#include <vtkm/Types.h> #include <vtkm/Types.h>
#include <vtkm/cont/ArrayCopy.h> #include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayHandle.h> #include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/worklet/WorkletMapField.h> #include <vtkm/worklet/WorkletPointNeighborhood.h>
namespace namespace
{ {
@ -25,101 +24,68 @@ struct TypeUInt8 : vtkm::ListTagBase<vtkm::UInt8>
{ {
}; };
class SetStructuredGhostCells1D : public vtkm::worklet::WorkletMapField class SetStructuredGhostCells1D : public vtkm::worklet::WorkletPointNeighborhood
{ {
public: public:
SetStructuredGhostCells1D(const vtkm::Id& dim, const vtkm::Id& numLayers = 1) SetStructuredGhostCells1D(vtkm::IdComponent numLayers = 1)
: Dim(dim) : NumLayers(numLayers)
, NumLayers(numLayers)
, Range(NumLayers, Dim - NumLayers)
{ {
} }
using ControlSignature = void(FieldIn, FieldOut); using ControlSignature = void(CellSetIn, FieldOut);
using ExecutionSignature = void(_1, _2); using ExecutionSignature = void(Boundary, _2);
template <typename T> VTKM_EXEC void operator()(const vtkm::exec::BoundaryState& boundary, vtkm::UInt8& value) const
VTKM_EXEC void operator()(const vtkm::Id& cellIndex, T& value) const
{ {
value = (Range.Contains(cellIndex) ? NormalCell : DuplicateCell); const bool notOnBoundary = boundary.IsRadiusInXBoundary(this->NumLayers);
value = (notOnBoundary) ? vtkm::CellClassification::NORMAL : vtkm::CellClassification::GHOST;
} }
private: private:
vtkm::Id Dim; vtkm::IdComponent NumLayers;
vtkm::Id NumLayers;
vtkm::RangeId Range;
static constexpr vtkm::UInt8 NormalCell =
static_cast<vtkm::UInt8>(vtkm::CellClassification::NORMAL);
static constexpr vtkm::UInt8 DuplicateCell =
static_cast<vtkm::UInt8>(vtkm::CellClassification::GHOST);
}; };
class SetStructuredGhostCells2D : public vtkm::worklet::WorkletMapField class SetStructuredGhostCells2D : public vtkm::worklet::WorkletPointNeighborhood
{ {
public: public:
SetStructuredGhostCells2D(const vtkm::Id2& dims, const vtkm::Id& numLayers = 1) SetStructuredGhostCells2D(vtkm::IdComponent numLayers = 1)
: Dims(dims) : NumLayers(numLayers)
, NumLayers(numLayers)
, Range(NumLayers, Dims[0] - NumLayers, NumLayers, Dims[1] - NumLayers)
{ {
} }
using ControlSignature = void(FieldIn, FieldOut); using ControlSignature = void(CellSetIn, FieldOut);
using ExecutionSignature = void(_1, _2); using ExecutionSignature = void(Boundary, _2);
template <typename T> VTKM_EXEC void operator()(const vtkm::exec::BoundaryState& boundary, vtkm::UInt8& value) const
VTKM_EXEC void operator()(const vtkm::Id& cellIndex, T& value) const
{ {
vtkm::Id2 ij(cellIndex % Dims[0], cellIndex / Dims[0]); const bool notOnBoundary = boundary.IsRadiusInXBoundary(this->NumLayers) &&
boundary.IsRadiusInYBoundary(this->NumLayers);
value = (Range.Contains(ij) ? NormalCell : DuplicateCell); value = (notOnBoundary) ? vtkm::CellClassification::NORMAL : vtkm::CellClassification::GHOST;
} }
private: private:
vtkm::Id2 Dims; vtkm::IdComponent NumLayers;
vtkm::Id NumLayers;
vtkm::RangeId2 Range;
static constexpr vtkm::UInt8 NormalCell =
static_cast<vtkm::UInt8>(vtkm::CellClassification::NORMAL);
static constexpr vtkm::UInt8 DuplicateCell =
static_cast<vtkm::UInt8>(vtkm::CellClassification::GHOST);
}; };
class SetStructuredGhostCells3D : public vtkm::worklet::WorkletMapField class SetStructuredGhostCells3D : public vtkm::worklet::WorkletPointNeighborhood
{ {
public: public:
SetStructuredGhostCells3D(const vtkm::Id3& dims, const vtkm::Id& numLayers = 1) SetStructuredGhostCells3D(vtkm::IdComponent numLayers = 1)
: Dims(dims) : NumLayers(numLayers)
, NumLayers(numLayers)
, Range(NumLayers,
Dims[0] - NumLayers,
NumLayers,
Dims[1] - NumLayers,
NumLayers,
Dims[2] - NumLayers)
{ {
} }
using ControlSignature = void(FieldIn, FieldOut); using ControlSignature = void(CellSetIn, FieldOut);
using ExecutionSignature = void(_1, _2); using ExecutionSignature = void(Boundary, _2);
template <typename T> VTKM_EXEC void operator()(const vtkm::exec::BoundaryState& boundary, vtkm::UInt8& value) const
VTKM_EXEC void operator()(const vtkm::Id& cellIndex, T& value) const
{ {
vtkm::Id3 ijk( const bool notOnBoundary = boundary.IsRadiusInBoundary(this->NumLayers);
cellIndex % Dims[0], (cellIndex / Dims[0]) % Dims[1], cellIndex / (Dims[0] * Dims[1])); value = (notOnBoundary) ? vtkm::CellClassification::NORMAL : vtkm::CellClassification::GHOST;
value = (Range.Contains(ijk) ? NormalCell : DuplicateCell);
} }
private: private:
vtkm::Id3 Dims; vtkm::IdComponent NumLayers;
vtkm::Id NumLayers;
vtkm::RangeId3 Range;
static constexpr vtkm::UInt8 NormalCell =
static_cast<vtkm::UInt8>(vtkm::CellClassification::NORMAL);
static constexpr vtkm::UInt8 DuplicateCell =
static_cast<vtkm::UInt8>(vtkm::CellClassification::GHOST);
}; };
}; };
@ -137,11 +103,8 @@ inline VTKM_CONT vtkm::cont::DataSet GhostCellClassify::DoExecute(const vtkm::co
vtkm::filter::PolicyBase<Policy>) vtkm::filter::PolicyBase<Policy>)
{ {
const vtkm::cont::DynamicCellSet& cellset = input.GetCellSet(); const vtkm::cont::DynamicCellSet& cellset = input.GetCellSet();
vtkm::Id numCells = cellset.GetNumberOfCells();
vtkm::cont::ArrayHandleIndex indexArray(numCells);
vtkm::cont::ArrayHandle<vtkm::UInt8> ghosts; vtkm::cont::ArrayHandle<vtkm::UInt8> ghosts;
const vtkm::Id numCells = cellset.GetNumberOfCells();
ghosts.Allocate(numCells);
//Structured cases are easy... //Structured cases are easy...
if (cellset.template IsType<vtkm::cont::CellSetStructured<1>>()) if (cellset.template IsType<vtkm::cont::CellSetStructured<1>>())
@ -150,8 +113,12 @@ inline VTKM_CONT vtkm::cont::DataSet GhostCellClassify::DoExecute(const vtkm::co
throw vtkm::cont::ErrorFilterExecution("insufficient number of cells for GhostCellClassify."); throw vtkm::cont::ErrorFilterExecution("insufficient number of cells for GhostCellClassify.");
vtkm::cont::CellSetStructured<1> cellset1d = cellset.Cast<vtkm::cont::CellSetStructured<1>>(); vtkm::cont::CellSetStructured<1> cellset1d = cellset.Cast<vtkm::cont::CellSetStructured<1>>();
SetStructuredGhostCells1D structuredGhosts1D(cellset1d.GetCellDimensions());
this->Invoke(structuredGhosts1D, indexArray, ghosts); //We use the dual of the cellset since we are using the PointNeighborhood worklet
vtkm::cont::CellSetStructured<3> dual;
const auto dim = cellset1d.GetCellDimensions();
dual.SetPointDimensions(vtkm::Id3{ dim, 1, 1 });
this->Invoke(SetStructuredGhostCells1D{}, dual, ghosts);
} }
else if (cellset.template IsType<vtkm::cont::CellSetStructured<2>>()) else if (cellset.template IsType<vtkm::cont::CellSetStructured<2>>())
{ {
@ -159,8 +126,12 @@ inline VTKM_CONT vtkm::cont::DataSet GhostCellClassify::DoExecute(const vtkm::co
throw vtkm::cont::ErrorFilterExecution("insufficient number of cells for GhostCellClassify."); throw vtkm::cont::ErrorFilterExecution("insufficient number of cells for GhostCellClassify.");
vtkm::cont::CellSetStructured<2> cellset2d = cellset.Cast<vtkm::cont::CellSetStructured<2>>(); vtkm::cont::CellSetStructured<2> cellset2d = cellset.Cast<vtkm::cont::CellSetStructured<2>>();
SetStructuredGhostCells2D structuredGhosts2D(cellset2d.GetCellDimensions());
this->Invoke(structuredGhosts2D, indexArray, ghosts); //We use the dual of the cellset since we are using the PointNeighborhood worklet
vtkm::cont::CellSetStructured<3> dual;
const auto dims = cellset2d.GetCellDimensions();
dual.SetPointDimensions(vtkm::Id3{ dims[0], dims[1], 1 });
this->Invoke(SetStructuredGhostCells2D{}, dual, ghosts);
} }
else if (cellset.template IsType<vtkm::cont::CellSetStructured<3>>()) else if (cellset.template IsType<vtkm::cont::CellSetStructured<3>>())
{ {
@ -168,8 +139,11 @@ inline VTKM_CONT vtkm::cont::DataSet GhostCellClassify::DoExecute(const vtkm::co
throw vtkm::cont::ErrorFilterExecution("insufficient number of cells for GhostCellClassify."); throw vtkm::cont::ErrorFilterExecution("insufficient number of cells for GhostCellClassify.");
vtkm::cont::CellSetStructured<3> cellset3d = cellset.Cast<vtkm::cont::CellSetStructured<3>>(); vtkm::cont::CellSetStructured<3> cellset3d = cellset.Cast<vtkm::cont::CellSetStructured<3>>();
SetStructuredGhostCells3D structuredGhosts3D(cellset3d.GetCellDimensions());
this->Invoke(structuredGhosts3D, indexArray, ghosts); //We use the dual of the cellset since we are using the PointNeighborhood worklet
vtkm::cont::CellSetStructured<3> dual;
dual.SetPointDimensions(cellset3d.GetCellDimensions());
this->Invoke(SetStructuredGhostCells3D{}, dual, ghosts);
} }
else else
{ {

@ -68,16 +68,17 @@ void TestStructured()
std::cout << "Testing ghost cells for structured datasets." << std::endl; std::cout << "Testing ghost cells for structured datasets." << std::endl;
// specify some 2d tests: {numI, numJ, numK, numGhostLayers}. // specify some 2d tests: {numI, numJ, numK, numGhostLayers}.
std::vector<std::vector<vtkm::Id>> tests2D = { { 8, 4, 0, 1 }, { 5, 5, 0, 1 }, { 10, 10, 0, 1 },
{ 10, 5, 0, 1 }, { 5, 10, 0, 1 }, { 20, 10, 0, 1 },
{ 10, 20, 0, 1 } };
std::vector<std::vector<vtkm::Id>> tests3D = { { 8, 8, 10, 1 }, { 5, 5, 5, 1 },
{ 10, 10, 10, 1 }, { 10, 5, 10, 1 },
{ 5, 10, 10, 1 }, { 20, 10, 10, 1 },
{ 10, 20, 10, 1 } };
std::vector<std::vector<vtkm::Id>> tests1D = { std::vector<std::vector<vtkm::Id>> tests1D = {
{ 8, 0, 0, 1 }, { 5, 0, 0, 1 }, { 10, 0, 0, 1 }, { 20, 0, 0, 1 } { 8, 0, 0, 1 }, { 5, 0, 0, 1 }, { 10, 0, 0, 1 }, { 20, 0, 0, 1 }
}; };
std::vector<std::vector<vtkm::Id>> tests2D = { { 8, 4, 0, 1 }, { 5, 5, 0, 1 }, { 10, 10, 0, 1 },
{ 10, 5, 0, 1 }, { 5, 10, 0, 1 }, { 20, 10, 0, 1 },
{ 10, 20, 0, 1 } };
std::vector<std::vector<vtkm::Id>> tests3D = { { 8, 8, 10, 1 }, { 5, 5, 5, 1 },
{ 10, 10, 10, 1 }, { 10, 5, 10, 1 },
{ 5, 10, 10, 1 }, { 20, 10, 10, 1 },
{ 10, 20, 10, 1 }, { 1024, 512, 512, 1 },
{ 1024, 768, 10, 1 } };
std::vector<std::vector<vtkm::Id>> tests; std::vector<std::vector<vtkm::Id>> tests;
@ -119,7 +120,7 @@ void TestStructured()
vtkm::Id numNormalCells = 0; vtkm::Id numNormalCells = 0;
auto portal = ghostArray.GetPortalConstControl(); auto portal = ghostArray.GetPortalConstControl();
const vtkm::UInt8 normalCell = static_cast<vtkm::UInt8>(vtkm::CellClassification::NORMAL); constexpr vtkm::UInt8 normalCell = vtkm::CellClassification::NORMAL;
for (vtkm::Id i = 0; i < numCells; i++) for (vtkm::Id i = 0; i < numCells; i++)
if (portal.Get(i) == normalCell) if (portal.Get(i) == normalCell)
numNormalCells++; numNormalCells++;

@ -33,8 +33,8 @@ static vtkm::cont::ArrayHandle<vtkm::UInt8> StructuredGhostCellArray(vtkm::Id nx
if (nz > 0) if (nz > 0)
numCells *= nz; numCells *= nz;
vtkm::UInt8 normalCell = static_cast<vtkm::UInt8>(vtkm::CellClassification::NORMAL); constexpr vtkm::UInt8 normalCell = vtkm::CellClassification::NORMAL;
vtkm::UInt8 duplicateCell = static_cast<vtkm::UInt8>(vtkm::CellClassification::GHOST); constexpr vtkm::UInt8 duplicateCell = vtkm::CellClassification::GHOST;
vtkm::cont::ArrayHandle<vtkm::UInt8> ghosts; vtkm::cont::ArrayHandle<vtkm::UInt8> ghosts;
ghosts.Allocate(numCells); ghosts.Allocate(numCells);
@ -261,10 +261,9 @@ void TestGhostCellRemove()
if (rt == "all") if (rt == "all")
ghostCellRemoval.RemoveAllGhost(); ghostCellRemoval.RemoveAllGhost();
else if (rt == "byType") else if (rt == "byType")
ghostCellRemoval.RemoveByType( ghostCellRemoval.RemoveByType(vtkm::CellClassification::GHOST);
static_cast<vtkm::UInt8>(vtkm::CellClassification::GHOST));
auto output = ghostCellRemoval.Execute(ds, vtkm::filter::GhostCellRemovePolicy()); auto output = ghostCellRemoval.Execute(ds);
vtkm::Id numCells = output.GetNumberOfCells(); vtkm::Id numCells = output.GetNumberOfCells();
//Validate the output. //Validate the output.
@ -305,7 +304,7 @@ void TestGhostCellRemove()
vtkm::filter::GhostCellRemove ghostCellRemoval; vtkm::filter::GhostCellRemove ghostCellRemoval;
ghostCellRemoval.RemoveGhostField(); ghostCellRemoval.RemoveGhostField();
auto output = ghostCellRemoval.Execute(ds, vtkm::filter::GhostCellRemovePolicy()); auto output = ghostCellRemoval.Execute(ds);
VTKM_TEST_ASSERT(output.GetCellSet().IsType<vtkm::cont::CellSetExplicit<>>(), VTKM_TEST_ASSERT(output.GetCellSet().IsType<vtkm::cont::CellSetExplicit<>>(),
"Wrong cell type for explicit conversion"); "Wrong cell type for explicit conversion");
} }

@ -7,8 +7,8 @@
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information. // PURPOSE. See the above copyright notice for more information.
//============================================================================ //============================================================================
#ifndef vtk_m_worklet_WorkletPointNeigborhood_h #ifndef vtk_m_worklet_WorkletPointNeighborhood_h
#define vtk_m_worklet_WorkletPointNeigborhood_h #define vtk_m_worklet_WorkletPointNeighborhood_h
/// \brief Worklet for volume algorithms that require a neighborhood /// \brief Worklet for volume algorithms that require a neighborhood
/// ///