From e74c0732c5ec2ee332b3049f76676c5e3122b81b Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Wed, 18 Aug 2021 13:55:19 -0600 Subject: [PATCH] Compile reverse connectivity builder into vtkm_cont library Because `CellSetExplicit` is a templated class, the implementation of most of its features is part of the header files. One of the things that was included was the code to build the reverse connectivity links. That is, it figured out which cells were incident on each point using the standard connections of which points comprise which cells. Of course, building these links is non-trivial, and it used multiple DPPs to engage the device. It meant that header had to include the device adapter algorithms and therefore required a device compiler. We want to minimize this where possible. To get around this issue, a non-templated function was added to find the reverse connections of a `CellSetExplicit`. It does this by passing in `UnknownArrayHandle`s for the input arrays. (The output visit-points- with-cells arrays are standard across all template instances.) The implementation first iterates over all `CellSetExplicit` versions in `VTKM_DEFAULT_CELL_SETS` and attempts to retrieve arrays of those types. In the unlikely event that none of these arrays work, it copies the data to `ArrayHandle` and uses those. --- .../reverseconnection-precompiled.md | 21 +++ vtkm/cont/CellSetExplicit.cxx | 127 ++++++++++++++- vtkm/cont/CellSetExplicit.h | 36 ++++- vtkm/cont/CellSetExplicit.hxx | 72 --------- vtkm/cont/CellSetPermutation.h | 1 + .../internal/ConnectivityExplicitInternals.h | 149 ------------------ .../internal/ReverseConnectivityBuilder.h | 147 +++++++++++++++++ 7 files changed, 326 insertions(+), 227 deletions(-) create mode 100644 docs/changelog/reverseconnection-precompiled.md diff --git a/docs/changelog/reverseconnection-precompiled.md b/docs/changelog/reverseconnection-precompiled.md new file mode 100644 index 000000000..064eb9965 --- /dev/null +++ b/docs/changelog/reverseconnection-precompiled.md @@ -0,0 +1,21 @@ +# Compile reverse connectivity builder into vtkm_cont library + +Because `CellSetExplicit` is a templated class, the implementation of +most of its features is part of the header files. One of the things that +was included was the code to build the reverse connectivity links. That +is, it figured out which cells were incident on each point using the +standard connections of which points comprise which cells. + +Of course, building these links is non-trivial, and it used multiple +DPPs to engage the device. It meant that header had to include the +device adapter algorithms and therefore required a device compiler. We +want to minimize this where possible. + +To get around this issue, a non-templated function was added to find the +reverse connections of a `CellSetExplicit`. It does this by passing in +`UnknownArrayHandle`s for the input arrays. (The output visit-points- +with-cells arrays are standard across all template instances.) The +implementation first iterates over all `CellSetExplicit` versions in +`VTKM_DEFAULT_CELL_SETS` and attempts to retrieve arrays of those types. +In the unlikely event that none of these arrays work, it copies the data +to `ArrayHandle` and uses those. diff --git a/vtkm/cont/CellSetExplicit.cxx b/vtkm/cont/CellSetExplicit.cxx index 9f0c5a3ab..3f76f4db2 100644 --- a/vtkm/cont/CellSetExplicit.cxx +++ b/vtkm/cont/CellSetExplicit.cxx @@ -11,6 +11,91 @@ #include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +template +void DoBuildReverseConnectivity( + const vtkm::cont::ArrayHandle& connections, + const vtkm::cont::ArrayHandle& offsets, + vtkm::Id numberOfPoints, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit& visitPointsWithCells, + vtkm::cont::DeviceAdapterId suggestedDevice) +{ + using CellsWithPointsConnectivity = vtkm::cont::internal:: + ConnectivityExplicitInternals; + + // Make a fake visitCellsWithPoints to pass to ComputeRConnTable. This is a bit of a + // patchwork from changing implementation. + CellsWithPointsConnectivity visitCellsWithPoints; + visitCellsWithPoints.ElementsValid = true; + visitCellsWithPoints.Connectivity = connections; + visitCellsWithPoints.Offsets = offsets; + + bool success = + vtkm::cont::TryExecuteOnDevice(suggestedDevice, [&](vtkm::cont::DeviceAdapterId realDevice) { + vtkm::cont::internal::ComputeRConnTable( + visitPointsWithCells, visitCellsWithPoints, numberOfPoints, realDevice); + return true; + }); + + if (!success) + { + throw vtkm::cont::ErrorExecution("Failed to run CellSetExplicit reverse " + "connectivity builder."); + } +} + +struct BuildReverseConnectivityForCellSetType +{ + template + void operator()( + const vtkm::cont::CellSetExplicit&, + const vtkm::cont::UnknownArrayHandle& connections, + const vtkm::cont::UnknownArrayHandle& offsets, + vtkm::Id numberOfPoints, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit& visitPointsWithCells, + vtkm::cont::DeviceAdapterId device) + { + if (visitPointsWithCells.ElementsValid) + { + return; // Already computed reverse + } + + using ConnectArrayType = vtkm::cont::ArrayHandle; + using OffsetArrayType = vtkm::cont::ArrayHandle; + if (connections.CanConvert() && offsets.CanConvert()) + { + DoBuildReverseConnectivity(connections.AsArrayHandle(), + offsets.AsArrayHandle(), + numberOfPoints, + visitPointsWithCells, + device); + } + } + + template + void operator()(const CellSetType&, + const vtkm::cont::UnknownArrayHandle&, + const vtkm::cont::UnknownArrayHandle&, + vtkm::Id, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit&, + vtkm::cont::DeviceAdapterId) + { + // Not an explicit cell set, so skip. + } +}; + +} // anonymous namespace + namespace vtkm { namespace cont @@ -21,5 +106,45 @@ template class VTKM_CONT_EXPORT CellSetExplicit::StorageTag, VTKM_DEFAULT_CONNECTIVITY_STORAGE_TAG, typename vtkm::cont::ArrayHandleCounting::StorageTag>; + +namespace detail +{ + +void BuildReverseConnectivity( + const vtkm::cont::UnknownArrayHandle& connections, + const vtkm::cont::UnknownArrayHandle& offsets, + vtkm::Id numberOfPoints, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit& visitPointsWithCells, + vtkm::cont::DeviceAdapterId device) +{ + if (visitPointsWithCells.ElementsValid) + { + return; // Already computed + } + + vtkm::ListForEach(BuildReverseConnectivityForCellSetType{}, + VTKM_DEFAULT_CELL_SET_LIST{}, + connections, + offsets, + numberOfPoints, + visitPointsWithCells, + device); + + if (!visitPointsWithCells.ElementsValid) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "BuildReverseConnectivity failed for all known cell set types. " + "Attempting to copy connectivity arrays."); + vtkm::cont::ArrayHandle connectionsCopy; + vtkm::cont::ArrayCopy(connections, connectionsCopy); + vtkm::cont::ArrayHandle offsetsCopy; + vtkm::cont::ArrayCopy(offsets, offsetsCopy); + DoBuildReverseConnectivity( + connectionsCopy, offsetsCopy, numberOfPoints, visitPointsWithCells, device); + } } -} + +} // namespace vtkm::cont::detail + +} // namespace vtkm::cont +} // namespace vtkm diff --git a/vtkm/cont/CellSetExplicit.h b/vtkm/cont/CellSetExplicit.h index 166dbeee7..3a290e44b 100644 --- a/vtkm/cont/CellSetExplicit.h +++ b/vtkm/cont/CellSetExplicit.h @@ -11,6 +11,7 @@ #define vtk_m_cont_CellSetExplicit_h #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include @@ -37,6 +39,20 @@ struct CellSetExplicitConnectivityChooser using ConnectivityType = vtkm::cont::internal::ConnectivityExplicitInternals<>; }; +// The connectivity generally used for the visit-points-with-cells connectivity. +// This type of connectivity does not have variable shape types, and since it is +// never really provided externally we can use the defaults for the other arrays. +using DefaultVisitPointsWithCellsConnectivityExplicit = + vtkm::cont::internal::ConnectivityExplicitInternals< + typename ArrayHandleConstant::StorageTag>; + +VTKM_CONT_EXPORT void BuildReverseConnectivity( + const vtkm::cont::UnknownArrayHandle& connections, + const vtkm::cont::UnknownArrayHandle& offsets, + vtkm::Id numberOfPoints, + vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit& visitPointsWithCells, + vtkm::cont::DeviceAdapterId device); + } // namespace detail #ifndef VTKM_DEFAULT_SHAPES_STORAGE_TAG @@ -229,11 +245,22 @@ public: protected: VTKM_CONT void BuildConnectivity(vtkm::cont::DeviceAdapterId, vtkm::TopologyElementTagCell, - vtkm::TopologyElementTagPoint) const; + vtkm::TopologyElementTagPoint) const + { + VTKM_ASSERT(this->Data->CellPointIds.ElementsValid); + // no-op + } - VTKM_CONT void BuildConnectivity(vtkm::cont::DeviceAdapterId, + VTKM_CONT void BuildConnectivity(vtkm::cont::DeviceAdapterId device, vtkm::TopologyElementTagPoint, - vtkm::TopologyElementTagCell) const; + vtkm::TopologyElementTagCell) const + { + detail::BuildReverseConnectivity(this->Data->CellPointIds.Connectivity, + this->Data->CellPointIds.Offsets, + this->Data->NumberOfPoints, + this->Data->PointCellIds, + device); + } VTKM_CONT bool HasConnectivityImpl(vtkm::TopologyElementTagCell, vtkm::TopologyElementTagPoint) const @@ -336,8 +363,7 @@ struct CellSetExplicitConnectivityChooser::StorageTag>; + using ConnectivityType = vtkm::cont::detail::DefaultVisitPointsWithCellsConnectivityExplicit; }; } // namespace detail diff --git a/vtkm/cont/CellSetExplicit.hxx b/vtkm/cont/CellSetExplicit.hxx index 8a7e706ef..e9c5e76cc 100644 --- a/vtkm/cont/CellSetExplicit.hxx +++ b/vtkm/cont/CellSetExplicit.hxx @@ -9,14 +9,10 @@ //============================================================================ #ifndef vtk_m_cont_CellSetExplicit_hxx #define vtk_m_cont_CellSetExplicit_hxx -#include #include -#include #include #include -#include -#include // This file uses a lot of very verbose identifiers and the clang formatted // code quickly becomes unreadable. Stick with manual formatting for now. @@ -478,74 +474,6 @@ void CellSetExplicit::DeepCopy(const CellSet* src) this->Fill(other->GetNumberOfPoints(), shapes, conn, offsets); } -//---------------------------------------------------------------------------- - -namespace detail -{ - -template -struct BuildPointCellIdsFunctor -{ - BuildPointCellIdsFunctor(CellPointIdsT &cellPointIds, - PointCellIdsT &pointCellIds, - vtkm::Id numberOfPoints) - : CellPointIds(cellPointIds) - , PointCellIds(pointCellIds) - , NumberOfPoints(numberOfPoints) - { - } - - template - bool operator()(Device) const - { - internal::ComputeRConnTable(this->PointCellIds, - this->CellPointIds, - this->NumberOfPoints, - Device{}); - return true; - } - - CellPointIdsT &CellPointIds; - PointCellIdsT &PointCellIds; - vtkm::Id NumberOfPoints; -}; - -} // detail - -template -VTKM_CONT -void CellSetExplicit -::BuildConnectivity(vtkm::cont::DeviceAdapterId, - vtkm::TopologyElementTagCell, - vtkm::TopologyElementTagPoint) const -{ - VTKM_ASSERT(this->Data->CellPointIds.ElementsValid); - // no-op -} - -template -VTKM_CONT -void CellSetExplicit -::BuildConnectivity(vtkm::cont::DeviceAdapterId device, - vtkm::TopologyElementTagPoint, - vtkm::TopologyElementTagCell) const -{ - if (!this->Data->PointCellIds.ElementsValid) - { - auto self = const_cast(this); - using Func = detail::BuildPointCellIdsFunctor; - - auto functor = Func(self->Data->CellPointIds, - self->Data->PointCellIds, - self->Data->NumberOfPoints); - - if (!vtkm::cont::TryExecuteOnDevice(device, functor)) - { - throw vtkm::cont::ErrorExecution("Failed to run CellSetExplicit reverse " - "connectivity builder."); - } - } -} } } // vtkm::cont diff --git a/vtkm/cont/CellSetPermutation.h b/vtkm/cont/CellSetPermutation.h index 66f06f9e5..a22165fb6 100644 --- a/vtkm/cont/CellSetPermutation.h +++ b/vtkm/cont/CellSetPermutation.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include diff --git a/vtkm/cont/internal/ConnectivityExplicitInternals.h b/vtkm/cont/internal/ConnectivityExplicitInternals.h index 098890b44..f94cbb393 100644 --- a/vtkm/cont/internal/ConnectivityExplicitInternals.h +++ b/vtkm/cont/internal/ConnectivityExplicitInternals.h @@ -11,14 +11,10 @@ #define vtk_m_cont_internal_ConnectivityExplicitInternals_h #include -#include -#include #include #include #include #include -#include -#include namespace vtkm { @@ -82,151 +78,6 @@ struct ConnectivityExplicitInternals } } }; - -// Pass through (needed for ReverseConnectivityBuilder) -struct PassThrough -{ - VTKM_EXEC vtkm::Id operator()(const vtkm::Id& val) const { return val; } -}; - -// Compute cell id from input connectivity: -// Find the upper bound of the conn idx in the offsets table and subtract 1 -// -// Example: -// Offsets: | 0 | 3 | 6 | 10 | -// Conn: | 0 1 2 | 0 1 3 | 2 4 5 6 | 1 3 5 | -// ConnIdx: | 0 1 2 | 3 4 5 | 6 7 8 9 | 10 11 12 | -// UpprBnd: | 1 1 1 | 2 2 2 | 3 3 3 3 | 4 4 4 | -// CellIdx: | 0 0 0 | 1 1 1 | 2 2 2 2 | 3 3 3 | -template -struct ConnIdxToCellIdCalc -{ - OffsetsPortalType Offsets; - - VTKM_CONT - ConnIdxToCellIdCalc(const OffsetsPortalType& offsets) - : Offsets(offsets) - { - } - - VTKM_EXEC - vtkm::Id operator()(vtkm::Id inIdx) const - { - // Compute the upper bound index: - vtkm::Id upperBoundIdx; - { - vtkm::Id first = 0; - vtkm::Id length = this->Offsets.GetNumberOfValues(); - - while (length > 0) - { - vtkm::Id halfway = length / 2; - vtkm::Id pos = first + halfway; - vtkm::Id val = this->Offsets.Get(pos); - if (val <= inIdx) - { - first = pos + 1; - length -= halfway + 1; - } - else - { - length = halfway; - } - } - - upperBoundIdx = first; - } - - return upperBoundIdx - 1; - } -}; - -// Much easier for CellSetSingleType: -struct ConnIdxToCellIdCalcSingleType -{ - vtkm::IdComponent CellSize; - - VTKM_CONT - ConnIdxToCellIdCalcSingleType(vtkm::IdComponent cellSize) - : CellSize(cellSize) - { - } - - VTKM_EXEC - vtkm::Id operator()(vtkm::Id inIdx) const { return inIdx / this->CellSize; } -}; - -template -void ComputeRConnTable(RConnTableT& rConnTable, - const ConnTableT& connTable, - vtkm::Id numberOfPoints, - vtkm::cont::DeviceAdapterId device) -{ - if (rConnTable.ElementsValid) - { - return; - } - - const auto& conn = connTable.Connectivity; - auto& rConn = rConnTable.Connectivity; - auto& rOffsets = rConnTable.Offsets; - const vtkm::Id rConnSize = conn.GetNumberOfValues(); - - { - vtkm::cont::Token token; - const auto offInPortal = connTable.Offsets.PrepareForInput(device, token); - - PassThrough idxCalc{}; - ConnIdxToCellIdCalc cellIdCalc{ offInPortal }; - - vtkm::cont::internal::ReverseConnectivityBuilder builder; - builder.Run(conn, rConn, rOffsets, idxCalc, cellIdCalc, numberOfPoints, rConnSize, device); - } - - rConnTable.Shapes = vtkm::cont::make_ArrayHandleConstant( - static_cast(CELL_SHAPE_VERTEX), numberOfPoints); - rConnTable.ElementsValid = true; -} - -// Specialize for CellSetSingleType: -template -void ComputeRConnTable(RConnTableT& rConnTable, - const ConnectivityExplicitInternals< // SingleType specialization types: - typename vtkm::cont::ArrayHandleConstant::StorageTag, - ConnectivityStorageTag, - typename vtkm::cont::ArrayHandleCounting::StorageTag>& connTable, - vtkm::Id numberOfPoints, - vtkm::cont::DeviceAdapterId device) -{ - if (rConnTable.ElementsValid) - { - return; - } - - const auto& conn = connTable.Connectivity; - auto& rConn = rConnTable.Connectivity; - auto& rOffsets = rConnTable.Offsets; - const vtkm::Id rConnSize = conn.GetNumberOfValues(); - - const vtkm::IdComponent cellSize = [&]() -> vtkm::IdComponent { - if (connTable.Offsets.GetNumberOfValues() >= 2) - { - const auto firstTwo = vtkm::cont::ArrayGetValues({ 0, 1 }, connTable.Offsets); - return static_cast(firstTwo[1] - firstTwo[0]); - } - return 0; - }(); - - PassThrough idxCalc{}; - ConnIdxToCellIdCalcSingleType cellIdCalc{ cellSize }; - - vtkm::cont::internal::ReverseConnectivityBuilder builder; - builder.Run(conn, rConn, rOffsets, idxCalc, cellIdCalc, numberOfPoints, rConnSize, device); - - rConnTable.Shapes = vtkm::cont::make_ArrayHandleConstant( - static_cast(CELL_SHAPE_VERTEX), numberOfPoints); - rConnTable.ElementsValid = true; -} } } } // namespace vtkm::cont::internal diff --git a/vtkm/cont/internal/ReverseConnectivityBuilder.h b/vtkm/cont/internal/ReverseConnectivityBuilder.h index e18695ed0..b0a520a0b 100644 --- a/vtkm/cont/internal/ReverseConnectivityBuilder.h +++ b/vtkm/cont/internal/ReverseConnectivityBuilder.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -211,6 +212,152 @@ public: } } }; + +// Pass through (needed for ReverseConnectivityBuilder) +struct PassThrough +{ + VTKM_EXEC vtkm::Id operator()(const vtkm::Id& val) const { return val; } +}; + +// Compute cell id from input connectivity: +// Find the upper bound of the conn idx in the offsets table and subtract 1 +// +// Example: +// Offsets: | 0 | 3 | 6 | 10 | +// Conn: | 0 1 2 | 0 1 3 | 2 4 5 6 | 1 3 5 | +// ConnIdx: | 0 1 2 | 3 4 5 | 6 7 8 9 | 10 11 12 | +// UpprBnd: | 1 1 1 | 2 2 2 | 3 3 3 3 | 4 4 4 | +// CellIdx: | 0 0 0 | 1 1 1 | 2 2 2 2 | 3 3 3 | +template +struct ConnIdxToCellIdCalc +{ + OffsetsPortalType Offsets; + + VTKM_CONT + ConnIdxToCellIdCalc(const OffsetsPortalType& offsets) + : Offsets(offsets) + { + } + + VTKM_EXEC + vtkm::Id operator()(vtkm::Id inIdx) const + { + // Compute the upper bound index: + vtkm::Id upperBoundIdx; + { + vtkm::Id first = 0; + vtkm::Id length = this->Offsets.GetNumberOfValues(); + + while (length > 0) + { + vtkm::Id halfway = length / 2; + vtkm::Id pos = first + halfway; + vtkm::Id val = this->Offsets.Get(pos); + if (val <= inIdx) + { + first = pos + 1; + length -= halfway + 1; + } + else + { + length = halfway; + } + } + + upperBoundIdx = first; + } + + return upperBoundIdx - 1; + } +}; + +// Much easier for CellSetSingleType: +struct ConnIdxToCellIdCalcSingleType +{ + vtkm::IdComponent CellSize; + + VTKM_CONT + ConnIdxToCellIdCalcSingleType(vtkm::IdComponent cellSize) + : CellSize(cellSize) + { + } + + VTKM_EXEC + vtkm::Id operator()(vtkm::Id inIdx) const { return inIdx / this->CellSize; } +}; + +template +void ComputeRConnTable(RConnTableT& rConnTable, + const ConnTableT& connTable, + vtkm::Id numberOfPoints, + vtkm::cont::DeviceAdapterId device) +{ + if (rConnTable.ElementsValid) + { + return; + } + + const auto& conn = connTable.Connectivity; + auto& rConn = rConnTable.Connectivity; + auto& rOffsets = rConnTable.Offsets; + const vtkm::Id rConnSize = conn.GetNumberOfValues(); + + { + vtkm::cont::Token token; + const auto offInPortal = connTable.Offsets.PrepareForInput(device, token); + + PassThrough idxCalc{}; + ConnIdxToCellIdCalc cellIdCalc{ offInPortal }; + + vtkm::cont::internal::ReverseConnectivityBuilder builder; + builder.Run(conn, rConn, rOffsets, idxCalc, cellIdCalc, numberOfPoints, rConnSize, device); + } + + rConnTable.Shapes = vtkm::cont::make_ArrayHandleConstant( + static_cast(CELL_SHAPE_VERTEX), numberOfPoints); + rConnTable.ElementsValid = true; +} + +// Specialize for CellSetSingleType: +template +void ComputeRConnTable(RConnTableT& rConnTable, + const ConnectivityExplicitInternals< // SingleType specialization types: + typename vtkm::cont::ArrayHandleConstant::StorageTag, + ConnectivityStorageTag, + typename vtkm::cont::ArrayHandleCounting::StorageTag>& connTable, + vtkm::Id numberOfPoints, + vtkm::cont::DeviceAdapterId device) +{ + if (rConnTable.ElementsValid) + { + return; + } + + const auto& conn = connTable.Connectivity; + auto& rConn = rConnTable.Connectivity; + auto& rOffsets = rConnTable.Offsets; + const vtkm::Id rConnSize = conn.GetNumberOfValues(); + + const vtkm::IdComponent cellSize = [&]() -> vtkm::IdComponent { + if (connTable.Offsets.GetNumberOfValues() >= 2) + { + const auto firstTwo = vtkm::cont::ArrayGetValues({ 0, 1 }, connTable.Offsets); + return static_cast(firstTwo[1] - firstTwo[0]); + } + return 0; + }(); + + PassThrough idxCalc{}; + ConnIdxToCellIdCalcSingleType cellIdCalc{ cellSize }; + + vtkm::cont::internal::ReverseConnectivityBuilder builder; + builder.Run(conn, rConn, rOffsets, idxCalc, cellIdCalc, numberOfPoints, rConnSize, device); + + rConnTable.Shapes = vtkm::cont::make_ArrayHandleConstant( + static_cast(CELL_SHAPE_VERTEX), numberOfPoints); + rConnTable.ElementsValid = true; +} + } } } // end namespace vtkm::cont::internal