improve(CompositeArray): performance gain by flattening binary tree

The vtkCompositeArray performance was lagging due to the internal binary
tree structure. Flattening it out allowed for a considerable reduction
in the overhead of the composite array.
This commit is contained in:
Julien Fausty 2022-11-29 14:25:22 +01:00
parent 08c4b0ad52
commit d52d7d24cf
5 changed files with 46 additions and 74 deletions

View File

@ -37,7 +37,7 @@ int TestCompositeImplicitBackend(int, char*[])
std::iota(rightRange.begin(), rightRange.end(), 10);
// Make structure
vtkCompositeImplicitBackend<int> composite(left, right);
vtkCompositeImplicitBackend<int> composite(std::vector<vtkDataArray*>({ left, right }));
// Do checks on structure
for (int i = 0; i < 20; ++i)
@ -62,7 +62,8 @@ int TestCompositeImplicitBackend(int, char*[])
auto rightMultiRange = vtk::DataArrayValueRange<3>(rightMulti);
std::iota(rightMultiRange.begin(), rightMultiRange.end(), 30);
vtkCompositeImplicitBackend<int> compositeMulti(leftMulti, rightMulti);
vtkCompositeImplicitBackend<int> compositeMulti(
std::vector<vtkDataArray*>({ leftMulti, rightMulti }));
for (int i = 0; i < 60; ++i)
{

View File

@ -48,8 +48,9 @@
* rightArr->SetNumberOfComponents(1);
* rightArr->SetNumberOfTuples(1);
* rightArr->SetValue(0, 1);
* std::vector<vtkDataArray*> arrays({leftArr, rightArr});
* vtkNew<vtkCompositeArray<int>> compositeArr;
* compositeArr->SetBackend(std::make_shared<vtkCompositeImplicitBackend<int>>(leftArr, rightArr));
* compositeArr->SetBackend(std::make_shared<vtkCompositeImplicitBackend<int>>(arrays));
* compositeArr->SetNumberOfComponents(1);
* compositeArr->SetNumberOfTuples(2);
* CHECK(compositArr->GetValue(1) == 1);

View File

@ -14,14 +14,6 @@ vtkSmartPointer<vtkCompositeArray<T>> ConcatenateDataArrays(
{
return nullptr;
}
if (arrays.size() == 1)
{
vtkNew<vtkCompositeArray<T>> composite;
composite->SetBackend(std::make_shared<vtkCompositeImplicitBackend<T>>(arrays[0], nullptr));
composite->SetNumberOfComponents(arrays[0]->GetNumberOfComponents());
composite->SetNumberOfTuples(arrays[0]->GetNumberOfTuples());
return composite;
}
vtkIdType nComps = arrays[0]->GetNumberOfComponents();
for (auto arr : arrays)
{
@ -31,35 +23,14 @@ vtkSmartPointer<vtkCompositeArray<T>> ConcatenateDataArrays(
return nullptr;
}
}
std::vector<vtkSmartPointer<vtkDataArray>> lifetimeBuffer;
lifetimeBuffer.assign(arrays.begin(), arrays.end());
std::vector<vtkSmartPointer<vtkCompositeArray<T>>> newComps;
while (lifetimeBuffer.size() != 1)
{
newComps.clear();
for (int i = 0; i < static_cast<int>(lifetimeBuffer.size() - 1); i += 2)
{
vtkNew<vtkCompositeArray<T>> composite;
composite->SetBackend(
std::make_shared<vtkCompositeImplicitBackend<T>>(lifetimeBuffer[i], lifetimeBuffer[i + 1]));
composite->SetNumberOfComponents(lifetimeBuffer[i]->GetNumberOfComponents());
composite->SetNumberOfTuples(
lifetimeBuffer[i]->GetNumberOfTuples() + lifetimeBuffer[i + 1]->GetNumberOfTuples());
newComps.emplace_back(composite);
}
if (lifetimeBuffer.size() % 2 != 0)
{
vtkNew<vtkCompositeArray<T>> composite;
composite->SetBackend(
std::make_shared<vtkCompositeImplicitBackend<T>>(newComps.back(), lifetimeBuffer.back()));
composite->SetNumberOfComponents(lifetimeBuffer.back()->GetNumberOfComponents());
composite->SetNumberOfTuples(
newComps.back()->GetNumberOfTuples() + lifetimeBuffer.back()->GetNumberOfTuples());
newComps.back() = composite;
}
lifetimeBuffer.assign(newComps.begin(), newComps.end());
}
return newComps[0];
vtkNew<vtkCompositeArray<T>> composite;
composite->SetBackend(std::make_shared<vtkCompositeImplicitBackend<T>>(arrays));
composite->SetNumberOfComponents(nComps);
int nTuples = 0;
std::for_each(arrays.begin(), arrays.end(),
[&nTuples](vtkDataArray* arr) { nTuples += arr->GetNumberOfTuples(); });
composite->SetNumberOfTuples(nTuples);
return composite;
}
VTK_ABI_NAMESPACE_END
}

View File

@ -23,9 +23,9 @@
* This structure can be classified as a closure and can be called using syntax similar to a
* function call.
*
* The class implements a binary tree like structure for facilitating fast access.
* The class implements a flat binary tree like structure for facilitating fast access.
*
* At construction it takes two arrays in order to represent their concatenation.
* At construction it takes an array arrays in order to represent their concatenation.
*
* An example of potential usage in a vtkImplicitArray
* ```
@ -39,7 +39,8 @@
* rightArr->SetValue(0, 1);
* vtkNew<vtkImplicitArray<vtkCompositeImplicitBackend<int>>> compositeArr; // easier with
* `vtkNew<vtkCompositeArray<int>> compositeArr;` if applicable
* compositeArr->SetBackend(std::make_shared<vtkCompositeImplicitBackend<int>>(leftArr, rightArr));
* std::vector<vtkDataArray*> arrays({leftArr, rightArr});
* compositeArr->SetBackend(std::make_shared<vtkCompositeImplicitBackend<int>>(arrays));
* CHECK(compositArr->GetValue(1) == 1);
* ```
*
@ -48,7 +49,9 @@
* > information.
*/
#include "vtkCommonImplicitArraysModule.h"
#include <memory>
#include <vector>
VTK_ABI_NAMESPACE_BEGIN
class vtkDataArray;
@ -58,11 +61,10 @@ class vtkCompositeImplicitBackend
public:
/**
* Constructor for the backend
* @param leftArr the array starting the composite at index 0
* @param rightArr the array following the leftArr and starting at index
* @param arrays std::vector of arrays to composite together
* leftArr->GetNumberOfTuples()
*/
vtkCompositeImplicitBackend(vtkDataArray* leftArr, vtkDataArray* rightArr);
vtkCompositeImplicitBackend(const std::vector<vtkDataArray*>& arrays);
~vtkCompositeImplicitBackend();
/**

View File

@ -26,41 +26,37 @@ VTK_ABI_NAMESPACE_BEGIN
template <typename ValueType>
struct vtkCompositeImplicitBackend<ValueType>::Internals
{
Internals(vtkDataArray* leftArr, vtkDataArray* rightArr)
: Left(leftArr)
, Right(rightArr)
template <class Iterator>
Internals(Iterator first, Iterator last)
{
if (!this->Left && !this->Right)
this->Arrays.assign(first, last);
if (this->Arrays.size() > 0)
{
vtkWarningWithObjectMacro(nullptr, "Creating composite array with two nullptrs");
this->Ranges.resize(this->Arrays.size());
std::transform(first, last, this->Ranges.begin(),
[](vtkDataArray* arr) { return vtk::DataArrayValueRange(arr); });
this->Offsets.resize(this->Arrays.size() - 1);
std::size_t runningSum = 0;
std::transform(this->Ranges.begin(), this->Ranges.end() - 1, this->Offsets.begin(),
[&runningSum](vtk::detail::SelectValueRange<vtkDataArray*,
vtk::detail::DynamicTupleSize>::type& range) {
runningSum += range.size();
return runningSum;
});
}
auto checkNullRectify = [](vtkSmartPointer<vtkDataArray>& arr) {
if (!arr)
{
arr = vtkSmartPointer<vtkAOSDataArrayTemplate<ValueType>>::New();
arr->SetNumberOfComponents(1);
arr->SetNumberOfTuples(0);
}
};
checkNullRectify(this->Left);
checkNullRectify(this->Right);
this->LeftRange = vtk::DataArrayValueRange(this->Left);
this->RightRange = vtk::DataArrayValueRange(this->Right);
this->Offset = this->LeftRange.size();
}
vtkSmartPointer<vtkDataArray> Left;
vtk::detail::SelectValueRange<vtkDataArray*, vtk::detail::DynamicTupleSize>::type LeftRange;
vtkSmartPointer<vtkDataArray> Right;
vtk::detail::SelectValueRange<vtkDataArray*, vtk::detail::DynamicTupleSize>::type RightRange;
int Offset = -1;
std::vector<vtkSmartPointer<vtkDataArray>> Arrays;
std::vector<vtk::detail::SelectValueRange<vtkDataArray*, vtk::detail::DynamicTupleSize>::type>
Ranges;
std::vector<std::size_t> Offsets;
};
//-----------------------------------------------------------------------
template <typename ValueType>
vtkCompositeImplicitBackend<ValueType>::vtkCompositeImplicitBackend(
vtkDataArray* leftArr, vtkDataArray* rightArr)
: Internal(std::unique_ptr<Internals>(new Internals(leftArr, rightArr)))
const std::vector<vtkDataArray*>& arrays)
: Internal(std::unique_ptr<Internals>(new Internals(arrays.begin(), arrays.end())))
{
}
@ -72,8 +68,9 @@ vtkCompositeImplicitBackend<ValueType>::~vtkCompositeImplicitBackend() = default
template <typename ValueType>
ValueType vtkCompositeImplicitBackend<ValueType>::operator()(int idx) const
{
return static_cast<ValueType>((idx < this->Internal->Offset)
? this->Internal->LeftRange[idx]
: this->Internal->RightRange[idx - this->Internal->Offset]);
auto itPos =
std::upper_bound(this->Internal->Offsets.begin(), this->Internal->Offsets.end(), idx);
int locIdx = itPos == this->Internal->Offsets.begin() ? idx : idx - *(itPos - 1);
return this->Internal->Ranges[std::distance(this->Internal->Offsets.begin(), itPos)][locIdx];
}
VTK_ABI_NAMESPACE_END