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
{
enum struct CellClassification
enum CellClassification : vtkm::UInt8
{
NORMAL = 0, //Valid cell
GHOST = 1 << 0, //Ghost cell

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

@ -15,9 +15,8 @@
#include <vtkm/Types.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletPointNeighborhood.h>
namespace
{
@ -25,101 +24,68 @@ struct TypeUInt8 : vtkm::ListTagBase<vtkm::UInt8>
{
};
class SetStructuredGhostCells1D : public vtkm::worklet::WorkletMapField
class SetStructuredGhostCells1D : public vtkm::worklet::WorkletPointNeighborhood
{
public:
SetStructuredGhostCells1D(const vtkm::Id& dim, const vtkm::Id& numLayers = 1)
: Dim(dim)
, NumLayers(numLayers)
, Range(NumLayers, Dim - NumLayers)
SetStructuredGhostCells1D(vtkm::IdComponent numLayers = 1)
: NumLayers(numLayers)
{
}
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
using ControlSignature = void(CellSetIn, FieldOut);
using ExecutionSignature = void(Boundary, _2);
template <typename T>
VTKM_EXEC void operator()(const vtkm::Id& cellIndex, T& value) const
VTKM_EXEC void operator()(const vtkm::exec::BoundaryState& boundary, vtkm::UInt8& value) const
{
value = (Range.Contains(cellIndex) ? NormalCell : DuplicateCell);
const bool notOnBoundary = boundary.IsRadiusInXBoundary(this->NumLayers);
value = (notOnBoundary) ? vtkm::CellClassification::NORMAL : vtkm::CellClassification::GHOST;
}
private:
vtkm::Id Dim;
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);
vtkm::IdComponent NumLayers;
};
class SetStructuredGhostCells2D : public vtkm::worklet::WorkletMapField
class SetStructuredGhostCells2D : public vtkm::worklet::WorkletPointNeighborhood
{
public:
SetStructuredGhostCells2D(const vtkm::Id2& dims, const vtkm::Id& numLayers = 1)
: Dims(dims)
, NumLayers(numLayers)
, Range(NumLayers, Dims[0] - NumLayers, NumLayers, Dims[1] - NumLayers)
SetStructuredGhostCells2D(vtkm::IdComponent numLayers = 1)
: NumLayers(numLayers)
{
}
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
using ControlSignature = void(CellSetIn, FieldOut);
using ExecutionSignature = void(Boundary, _2);
template <typename T>
VTKM_EXEC void operator()(const vtkm::Id& cellIndex, T& value) const
VTKM_EXEC void operator()(const vtkm::exec::BoundaryState& boundary, vtkm::UInt8& value) const
{
vtkm::Id2 ij(cellIndex % Dims[0], cellIndex / Dims[0]);
value = (Range.Contains(ij) ? NormalCell : DuplicateCell);
const bool notOnBoundary = boundary.IsRadiusInXBoundary(this->NumLayers) &&
boundary.IsRadiusInYBoundary(this->NumLayers);
value = (notOnBoundary) ? vtkm::CellClassification::NORMAL : vtkm::CellClassification::GHOST;
}
private:
vtkm::Id2 Dims;
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);
vtkm::IdComponent NumLayers;
};
class SetStructuredGhostCells3D : public vtkm::worklet::WorkletMapField
class SetStructuredGhostCells3D : public vtkm::worklet::WorkletPointNeighborhood
{
public:
SetStructuredGhostCells3D(const vtkm::Id3& dims, const vtkm::Id& numLayers = 1)
: Dims(dims)
, NumLayers(numLayers)
, Range(NumLayers,
Dims[0] - NumLayers,
NumLayers,
Dims[1] - NumLayers,
NumLayers,
Dims[2] - NumLayers)
SetStructuredGhostCells3D(vtkm::IdComponent numLayers = 1)
: NumLayers(numLayers)
{
}
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
using ControlSignature = void(CellSetIn, FieldOut);
using ExecutionSignature = void(Boundary, _2);
template <typename T>
VTKM_EXEC void operator()(const vtkm::Id& cellIndex, T& value) const
VTKM_EXEC void operator()(const vtkm::exec::BoundaryState& boundary, vtkm::UInt8& value) const
{
vtkm::Id3 ijk(
cellIndex % Dims[0], (cellIndex / Dims[0]) % Dims[1], cellIndex / (Dims[0] * Dims[1]));
value = (Range.Contains(ijk) ? NormalCell : DuplicateCell);
const bool notOnBoundary = boundary.IsRadiusInBoundary(this->NumLayers);
value = (notOnBoundary) ? vtkm::CellClassification::NORMAL : vtkm::CellClassification::GHOST;
}
private:
vtkm::Id3 Dims;
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);
vtkm::IdComponent NumLayers;
};
};
@ -137,11 +103,8 @@ inline VTKM_CONT vtkm::cont::DataSet GhostCellClassify::DoExecute(const vtkm::co
vtkm::filter::PolicyBase<Policy>)
{
const vtkm::cont::DynamicCellSet& cellset = input.GetCellSet();
vtkm::Id numCells = cellset.GetNumberOfCells();
vtkm::cont::ArrayHandleIndex indexArray(numCells);
vtkm::cont::ArrayHandle<vtkm::UInt8> ghosts;
ghosts.Allocate(numCells);
const vtkm::Id numCells = cellset.GetNumberOfCells();
//Structured cases are easy...
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.");
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>>())
{
@ -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.");
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>>())
{
@ -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.");
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
{

@ -68,16 +68,17 @@ void TestStructured()
std::cout << "Testing ghost cells for structured datasets." << std::endl;
// 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 = {
{ 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;
@ -119,7 +120,7 @@ void TestStructured()
vtkm::Id numNormalCells = 0;
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++)
if (portal.Get(i) == normalCell)
numNormalCells++;

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

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