vtk-m/benchmarking/BenchmarkTopologyAlgorithms.cxx
Kenneth Moreland 9d5d9e38a1 Remove testing headers from benchmarking
The code in `vtkm/cont/Testing.h` now requires a library, which is not
built if testing is not built. Thus, the benchmarking code was giving a
compile error if benchmarking was on but testing was off.

Change the benchmarking to not rely on anything in the Testing
framework. This means using classes in `vtkm/source` instead of
`MakeTestData`. Also avoid using the `TestValue` defined for the tests.
(In one case, we have a simple replacement.) Also had to fix a problem
with a header file not defining everything it needed to compile.
2021-06-10 09:41:26 -06:00

404 lines
12 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include "Benchmarker.h"
#include <vtkm/Math.h>
#include <vtkm/VectorAnalysis.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/CellSetStructured.h>
#include <vtkm/cont/Invoker.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/cont/UncertainArrayHandle.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletMapTopology.h>
#include <cctype>
#include <random>
#include <string>
namespace
{
#define CUBE_SIZE 256
using ValueTypes = vtkm::List<vtkm::UInt32, vtkm::Int32, vtkm::Int64, vtkm::Float32, vtkm::Float64>;
using ValueUncertainHandle =
vtkm::cont::UncertainArrayHandle<ValueTypes, vtkm::cont::StorageListBasic>;
// Hold configuration state (e.g. active device)
vtkm::cont::InitializeResult Config;
class AveragePointToCell : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(FieldInPoint inPoints, CellSetIn cellset, FieldOutCell outCells);
using ExecutionSignature = void(_1, PointCount, _3);
using InputDomain = _2;
template <typename PointValueVecType, typename OutType>
VTKM_EXEC void operator()(const PointValueVecType& pointValues,
const vtkm::IdComponent& numPoints,
OutType& average) const
{
OutType sum = static_cast<OutType>(pointValues[0]);
for (vtkm::IdComponent pointIndex = 1; pointIndex < numPoints; ++pointIndex)
{
sum = sum + static_cast<OutType>(pointValues[pointIndex]);
}
average = sum / static_cast<OutType>(numPoints);
}
};
class AverageCellToPoint : public vtkm::worklet::WorkletVisitPointsWithCells
{
public:
using ControlSignature = void(FieldInCell inCells, CellSetIn topology, FieldOut outPoints);
using ExecutionSignature = void(_1, _3, CellCount);
using InputDomain = _2;
template <typename CellVecType, typename OutType>
VTKM_EXEC void operator()(const CellVecType& cellValues,
OutType& avgVal,
const vtkm::IdComponent& numCellIDs) const
{
//simple functor that returns the average cell Value.
avgVal = vtkm::TypeTraits<OutType>::ZeroInitialization();
if (numCellIDs != 0)
{
for (vtkm::IdComponent cellIndex = 0; cellIndex < numCellIDs; ++cellIndex)
{
avgVal += static_cast<OutType>(cellValues[cellIndex]);
}
avgVal = avgVal / static_cast<OutType>(numCellIDs);
}
}
};
// -----------------------------------------------------------------------------
template <typename T>
class Classification : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(FieldInPoint inNodes, CellSetIn cellset, FieldOutCell outCaseId);
using ExecutionSignature = void(_1, _3);
using InputDomain = _2;
T IsoValue;
VTKM_CONT
Classification(T isovalue)
: IsoValue(isovalue)
{
}
template <typename FieldInType>
VTKM_EXEC void operator()(const FieldInType& fieldIn, vtkm::IdComponent& caseNumber) const
{
using FieldType = typename vtkm::VecTraits<FieldInType>::ComponentType;
const FieldType iso = static_cast<FieldType>(this->IsoValue);
caseNumber = ((fieldIn[0] > iso) | (fieldIn[1] > iso) << 1 | (fieldIn[2] > iso) << 2 |
(fieldIn[3] > iso) << 3 | (fieldIn[4] > iso) << 4 | (fieldIn[5] > iso) << 5 |
(fieldIn[6] > iso) << 6 | (fieldIn[7] > iso) << 7);
}
};
template <typename T, typename Enable = void>
struct NumberGenerator
{
};
template <typename T>
struct NumberGenerator<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
{
std::mt19937 rng;
std::uniform_real_distribution<T> distribution;
NumberGenerator(T low, T high)
: rng()
, distribution(low, high)
{
}
T next() { return distribution(rng); }
};
template <typename T>
struct NumberGenerator<T, typename std::enable_if<!std::is_floating_point<T>::value>::type>
{
std::mt19937 rng;
std::uniform_int_distribution<T> distribution;
NumberGenerator(T low, T high)
: rng()
, distribution(low, high)
{
}
T next() { return distribution(rng); }
};
// Returns an extra random value.
// Like, an additional random value.
// Not a random value that's somehow "extra random".
template <typename ArrayT>
VTKM_CONT typename ArrayT::ValueType FillRandomValues(ArrayT& array,
vtkm::Id size,
vtkm::Float64 min,
vtkm::Float64 max)
{
using ValueType = typename ArrayT::ValueType;
NumberGenerator<ValueType> generator{ static_cast<ValueType>(min), static_cast<ValueType>(max) };
array.Allocate(size);
auto portal = array.WritePortal();
for (vtkm::Id i = 0; i < size; ++i)
{
portal.Set(i, generator.next());
}
return generator.next();
}
template <typename Value>
struct BenchCellToPointAvgImpl
{
vtkm::cont::ArrayHandle<Value> Input;
::benchmark::State& State;
vtkm::Id CubeSize;
vtkm::Id NumCells;
vtkm::cont::Timer Timer;
vtkm::cont::Invoker Invoker;
VTKM_CONT
BenchCellToPointAvgImpl(::benchmark::State& state)
: State{ state }
, CubeSize{ CUBE_SIZE }
, NumCells{ (this->CubeSize - 1) * (this->CubeSize - 1) * (this->CubeSize - 1) }
, Timer{ Config.Device }
, Invoker{ Config.Device }
{
FillRandomValues(this->Input, this->NumCells, 1., 100.);
{ // Configure label:
std::ostringstream desc;
desc << "CubeSize:" << this->CubeSize;
this->State.SetLabel(desc.str());
}
}
template <typename BenchArrayType>
VTKM_CONT void Run(const BenchArrayType& input)
{
vtkm::cont::CellSetStructured<3> cellSet;
cellSet.SetPointDimensions(vtkm::Id3{ this->CubeSize, this->CubeSize, this->CubeSize });
vtkm::cont::ArrayHandle<Value> result;
for (auto _ : this->State)
{
(void)_;
this->Timer.Start();
this->Invoker(AverageCellToPoint{}, input, cellSet, result);
this->Timer.Stop();
this->State.SetIterationTime(this->Timer.GetElapsedTime());
}
// #items = #points
const int64_t iterations = static_cast<int64_t>(this->State.iterations());
this->State.SetItemsProcessed(static_cast<int64_t>(cellSet.GetNumberOfPoints()) * iterations);
}
};
template <typename ValueType>
void BenchCellToPointAvgStatic(::benchmark::State& state)
{
BenchCellToPointAvgImpl<ValueType> impl{ state };
impl.Run(impl.Input);
};
VTKM_BENCHMARK_TEMPLATES(BenchCellToPointAvgStatic, ValueTypes);
template <typename ValueType>
void BenchCellToPointAvgDynamic(::benchmark::State& state)
{
BenchCellToPointAvgImpl<ValueType> impl{ state };
impl.Run(ValueUncertainHandle{ impl.Input });
};
VTKM_BENCHMARK_TEMPLATES(BenchCellToPointAvgDynamic, ValueTypes);
template <typename Value>
struct BenchPointToCellAvgImpl
{
vtkm::cont::ArrayHandle<Value> Input;
::benchmark::State& State;
vtkm::Id CubeSize;
vtkm::Id NumPoints;
vtkm::cont::Timer Timer;
vtkm::cont::Invoker Invoker;
VTKM_CONT
BenchPointToCellAvgImpl(::benchmark::State& state)
: State{ state }
, CubeSize{ CUBE_SIZE }
, NumPoints{ (this->CubeSize) * (this->CubeSize) * (this->CubeSize) }
, Timer{ Config.Device }
, Invoker{ Config.Device }
{
FillRandomValues(this->Input, this->NumPoints, 1., 100.);
{ // Configure label:
std::ostringstream desc;
desc << "CubeSize:" << this->CubeSize;
this->State.SetLabel(desc.str());
}
}
template <typename BenchArrayType>
VTKM_CONT void Run(const BenchArrayType& input)
{
vtkm::cont::CellSetStructured<3> cellSet;
cellSet.SetPointDimensions(vtkm::Id3{ this->CubeSize, this->CubeSize, this->CubeSize });
vtkm::cont::ArrayHandle<Value> result;
for (auto _ : this->State)
{
(void)_;
this->Timer.Start();
this->Invoker(AveragePointToCell{}, input, cellSet, result);
this->Timer.Stop();
this->State.SetIterationTime(this->Timer.GetElapsedTime());
}
// #items = #cells
const int64_t iterations = static_cast<int64_t>(this->State.iterations());
this->State.SetItemsProcessed(static_cast<int64_t>(cellSet.GetNumberOfCells()) * iterations);
}
};
template <typename ValueType>
void BenchPointToCellAvgStatic(::benchmark::State& state)
{
BenchPointToCellAvgImpl<ValueType> impl{ state };
impl.Run(impl.Input);
};
VTKM_BENCHMARK_TEMPLATES(BenchPointToCellAvgStatic, ValueTypes);
template <typename ValueType>
void BenchPointToCellAvgDynamic(::benchmark::State& state)
{
BenchPointToCellAvgImpl<ValueType> impl{ state };
impl.Run(ValueUncertainHandle{ impl.Input });
};
VTKM_BENCHMARK_TEMPLATES(BenchPointToCellAvgDynamic, ValueTypes);
template <typename Value>
struct BenchClassificationImpl
{
vtkm::cont::ArrayHandle<Value> Input;
::benchmark::State& State;
vtkm::Id CubeSize;
vtkm::Id DomainSize;
Value IsoValue;
vtkm::cont::Timer Timer;
vtkm::cont::Invoker Invoker;
VTKM_CONT
BenchClassificationImpl(::benchmark::State& state)
: State{ state }
, CubeSize{ CUBE_SIZE }
, DomainSize{ this->CubeSize * this->CubeSize * this->CubeSize }
, Timer{ Config.Device }
, Invoker{ Config.Device }
{
this->IsoValue = FillRandomValues(this->Input, this->DomainSize, 1., 100.);
{ // Configure label:
std::ostringstream desc;
desc << "CubeSize:" << this->CubeSize;
this->State.SetLabel(desc.str());
}
}
template <typename BenchArrayType>
VTKM_CONT void Run(const BenchArrayType& input)
{
vtkm::cont::CellSetStructured<3> cellSet;
cellSet.SetPointDimensions(vtkm::Id3{ this->CubeSize, this->CubeSize, this->CubeSize });
vtkm::cont::ArrayHandle<vtkm::IdComponent> result;
Classification<Value> worklet(this->IsoValue);
for (auto _ : this->State)
{
(void)_;
this->Timer.Start();
this->Invoker(worklet, input, cellSet, result);
this->Timer.Stop();
this->State.SetIterationTime(this->Timer.GetElapsedTime());
}
// #items = #cells
const int64_t iterations = static_cast<int64_t>(this->State.iterations());
this->State.SetItemsProcessed(static_cast<int64_t>(cellSet.GetNumberOfCells()) * iterations);
}
};
template <typename ValueType>
void BenchClassificationStatic(::benchmark::State& state)
{
BenchClassificationImpl<ValueType> impl{ state };
impl.Run(impl.Input);
};
VTKM_BENCHMARK_TEMPLATES(BenchClassificationStatic, ValueTypes);
template <typename ValueType>
void BenchClassificationDynamic(::benchmark::State& state)
{
BenchClassificationImpl<ValueType> impl{ state };
impl.Run(ValueUncertainHandle{ impl.Input });
};
VTKM_BENCHMARK_TEMPLATES(BenchClassificationDynamic, ValueTypes);
} // end anon namespace
int main(int argc, char* argv[])
{
// Parse VTK-m options:
auto opts = vtkm::cont::InitializeOptions::RequireDevice;
std::vector<char*> args(argv, argv + argc);
vtkm::bench::detail::InitializeArgs(&argc, args, opts);
// Parse VTK-m options:
Config = vtkm::cont::Initialize(argc, args.data(), opts);
// This occurs when it is help
if (opts == vtkm::cont::InitializeOptions::None)
{
std::cout << Config.Usage << std::endl;
}
else
{
vtkm::cont::GetRuntimeDeviceTracker().ForceDevice(Config.Device);
}
// handle benchmarking related args and run benchmarks:
VTKM_EXECUTE_BENCHMARKS(argc, args.data());
}