From 8c3a8da99be77fd017ae21166664e2a631eeaa60 Mon Sep 17 00:00:00 2001 From: Robert Maynard Date: Tue, 10 Sep 2019 15:00:53 -0400 Subject: [PATCH] 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 --- vtkm/CellClassification.h | 2 +- vtkm/filter/GhostCellClassify.h | 2 + vtkm/filter/GhostCellClassify.hxx | 120 +++++++----------- .../testing/UnitTestGhostCellClassify.cxx | 17 +-- .../testing/UnitTestGhostCellRemove.cxx | 11 +- vtkm/worklet/WorkletPointNeighborhood.h | 4 +- 6 files changed, 66 insertions(+), 90 deletions(-) diff --git a/vtkm/CellClassification.h b/vtkm/CellClassification.h index 69bc5bee3..ccd414695 100644 --- a/vtkm/CellClassification.h +++ b/vtkm/CellClassification.h @@ -13,7 +13,7 @@ namespace vtkm { -enum struct CellClassification +enum CellClassification : vtkm::UInt8 { NORMAL = 0, //Valid cell GHOST = 1 << 0, //Ghost cell diff --git a/vtkm/filter/GhostCellClassify.h b/vtkm/filter/GhostCellClassify.h index 1f0106591..512b54cbc 100644 --- a/vtkm/filter/GhostCellClassify.h +++ b/vtkm/filter/GhostCellClassify.h @@ -25,6 +25,8 @@ struct GhostCellClassifyPolicy : vtkm::filter::PolicyBase { public: + using SupportedTypes = vtkm::ListTagBase; + VTKM_CONT GhostCellClassify(); diff --git a/vtkm/filter/GhostCellClassify.hxx b/vtkm/filter/GhostCellClassify.hxx index 9c9eb3339..5f48436a4 100644 --- a/vtkm/filter/GhostCellClassify.hxx +++ b/vtkm/filter/GhostCellClassify.hxx @@ -15,9 +15,8 @@ #include #include #include -#include -#include +#include namespace { @@ -25,101 +24,68 @@ struct TypeUInt8 : vtkm::ListTagBase { }; -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 - 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::CellClassification::NORMAL); - static constexpr vtkm::UInt8 DuplicateCell = - static_cast(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 - 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::CellClassification::NORMAL); - static constexpr vtkm::UInt8 DuplicateCell = - static_cast(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 - 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::CellClassification::NORMAL); - static constexpr vtkm::UInt8 DuplicateCell = - static_cast(vtkm::CellClassification::GHOST); + vtkm::IdComponent NumLayers; }; }; @@ -137,11 +103,8 @@ inline VTKM_CONT vtkm::cont::DataSet GhostCellClassify::DoExecute(const vtkm::co vtkm::filter::PolicyBase) { const vtkm::cont::DynamicCellSet& cellset = input.GetCellSet(); - vtkm::Id numCells = cellset.GetNumberOfCells(); - vtkm::cont::ArrayHandleIndex indexArray(numCells); vtkm::cont::ArrayHandle ghosts; - - ghosts.Allocate(numCells); + const vtkm::Id numCells = cellset.GetNumberOfCells(); //Structured cases are easy... if (cellset.template IsType>()) @@ -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>(); - 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>()) { @@ -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>(); - 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>()) { @@ -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>(); - 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 { diff --git a/vtkm/filter/testing/UnitTestGhostCellClassify.cxx b/vtkm/filter/testing/UnitTestGhostCellClassify.cxx index f84325b02..125920d30 100644 --- a/vtkm/filter/testing/UnitTestGhostCellClassify.cxx +++ b/vtkm/filter/testing/UnitTestGhostCellClassify.cxx @@ -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> 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> 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> tests1D = { { 8, 0, 0, 1 }, { 5, 0, 0, 1 }, { 10, 0, 0, 1 }, { 20, 0, 0, 1 } }; + std::vector> 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> 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> tests; @@ -119,7 +120,7 @@ void TestStructured() vtkm::Id numNormalCells = 0; auto portal = ghostArray.GetPortalConstControl(); - const vtkm::UInt8 normalCell = static_cast(vtkm::CellClassification::NORMAL); + constexpr vtkm::UInt8 normalCell = vtkm::CellClassification::NORMAL; for (vtkm::Id i = 0; i < numCells; i++) if (portal.Get(i) == normalCell) numNormalCells++; diff --git a/vtkm/filter/testing/UnitTestGhostCellRemove.cxx b/vtkm/filter/testing/UnitTestGhostCellRemove.cxx index 971297e15..5fbcc3b45 100644 --- a/vtkm/filter/testing/UnitTestGhostCellRemove.cxx +++ b/vtkm/filter/testing/UnitTestGhostCellRemove.cxx @@ -33,8 +33,8 @@ static vtkm::cont::ArrayHandle StructuredGhostCellArray(vtkm::Id nx if (nz > 0) numCells *= nz; - vtkm::UInt8 normalCell = static_cast(vtkm::CellClassification::NORMAL); - vtkm::UInt8 duplicateCell = static_cast(vtkm::CellClassification::GHOST); + constexpr vtkm::UInt8 normalCell = vtkm::CellClassification::NORMAL; + constexpr vtkm::UInt8 duplicateCell = vtkm::CellClassification::GHOST; vtkm::cont::ArrayHandle ghosts; ghosts.Allocate(numCells); @@ -261,10 +261,9 @@ void TestGhostCellRemove() if (rt == "all") ghostCellRemoval.RemoveAllGhost(); else if (rt == "byType") - ghostCellRemoval.RemoveByType( - static_cast(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>(), "Wrong cell type for explicit conversion"); } diff --git a/vtkm/worklet/WorkletPointNeighborhood.h b/vtkm/worklet/WorkletPointNeighborhood.h index e5bbce166..098235b94 100644 --- a/vtkm/worklet/WorkletPointNeighborhood.h +++ b/vtkm/worklet/WorkletPointNeighborhood.h @@ -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 ///