//============================================================================ // 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. // // Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS). // Copyright 2014 UT-Battelle, LLC. // Copyright 2014 Los Alamos National Security. // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #include #include #include #include #include #include #include #include #include #include "Benchmarker.h" #include #include #include #include namespace vtkm { namespace benchmarking { #define CUBE_SIZE 256 static const std::string DIVIDER(40, '-'); enum BenchmarkName { CELL_TO_POINT = 1 << 1, POINT_TO_CELL = 1 << 2, MC_CLASSIFY = 1 << 3, ALL = CELL_TO_POINT | POINT_TO_CELL | MC_CLASSIFY }; class AveragePointToCell : public vtkm::worklet::WorkletMapPointToCell { public: using ControlSignature = void(FieldInPoint<> inPoints, CellSetIn cellset, FieldOutCell<> outCells); using ExecutionSignature = void(_1, PointCount, _3); using InputDomain = _2; template VTKM_EXEC void operator()(const PointValueVecType& pointValues, const vtkm::IdComponent& numPoints, OutType& average) const { OutType sum = static_cast(pointValues[0]); for (vtkm::IdComponent pointIndex = 1; pointIndex < numPoints; ++pointIndex) { sum = sum + static_cast(pointValues[pointIndex]); } average = sum / static_cast(numPoints); } }; class AverageCellToPoint : public vtkm::worklet::WorkletMapCellToPoint { public: using ControlSignature = void(FieldInCell<> inCells, CellSetIn topology, FieldOut<> outPoints); using ExecutionSignature = void(_1, _3, CellCount); using InputDomain = _2; template 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::ZeroInitialization(); if (numCellIDs != 0) { for (vtkm::IdComponent cellIndex = 0; cellIndex < numCellIDs; ++cellIndex) { avgVal += static_cast(cellValues[cellIndex]); } avgVal = avgVal / static_cast(numCellIDs); } } }; // ----------------------------------------------------------------------------- template class Classification : public vtkm::worklet::WorkletMapPointToCell { 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 VTKM_EXEC void operator()(const FieldInType& fieldIn, vtkm::IdComponent& caseNumber) const { using FieldType = typename vtkm::VecTraits::ComponentType; const FieldType iso = static_cast(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); } }; struct ValueTypes : vtkm::ListTagBase { }; /// This class runs a series of micro-benchmarks to measure /// performance of different field operations template class BenchmarkTopologyAlgorithms { using StorageTag = vtkm::cont::StorageTagBasic; using Timer = vtkm::cont::Timer; using ValueVariantHandle = vtkm::cont::VariantArrayHandleBase; private: template struct NumberGenerator { }; template struct NumberGenerator::value>::type> { std::mt19937 rng; std::uniform_real_distribution distribution; NumberGenerator(T low, T high) : rng() , distribution(low, high) { } T next() { return distribution(rng); } }; template struct NumberGenerator::value>::type> { std::mt19937 rng; std::uniform_int_distribution distribution; NumberGenerator(T low, T high) : rng() , distribution(low, high) { } T next() { return distribution(rng); } }; template struct BenchCellToPointAvg { std::vector input; vtkm::cont::ArrayHandle InputHandle; std::size_t DomainSize; VTKM_CONT BenchCellToPointAvg() { NumberGenerator generator(static_cast(1.0), static_cast(100.0)); //cube size is points in each dim this->DomainSize = (CUBE_SIZE - 1) * (CUBE_SIZE - 1) * (CUBE_SIZE - 1); this->input.resize(DomainSize); for (std::size_t i = 0; i < DomainSize; ++i) { this->input[i] = generator.next(); } this->InputHandle = vtkm::cont::make_ArrayHandle(this->input); } VTKM_CONT vtkm::Float64 operator()() { vtkm::cont::CellSetStructured<3> cellSet; cellSet.SetPointDimensions(vtkm::Id3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE)); vtkm::cont::ArrayHandle result; Timer timer; vtkm::worklet::DispatcherMapTopology dispatcher; dispatcher.SetDevice(DeviceAdapterTag()); dispatcher.Invoke(this->InputHandle, cellSet, result); return timer.GetElapsedTime(); } virtual std::string Type() const { return std::string("Static"); } VTKM_CONT std::string Description() const { std::stringstream description; description << "Computing Cell To Point Average " << "[" << this->Type() << "] " << "with a domain size of: " << this->DomainSize; return description.str(); } }; template struct BenchCellToPointAvgDynamic : public BenchCellToPointAvg { VTKM_CONT vtkm::Float64 operator()() { vtkm::cont::CellSetStructured<3> cellSet; cellSet.SetPointDimensions(vtkm::Id3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE)); ValueVariantHandle dinput(this->InputHandle); vtkm::cont::ArrayHandle result; Timer timer; vtkm::worklet::DispatcherMapTopology dispatcher; dispatcher.SetDevice(DeviceAdapterTag()); dispatcher.Invoke(dinput, cellSet, result); return timer.GetElapsedTime(); } virtual std::string Type() const { return std::string("Dynamic"); } }; VTKM_MAKE_BENCHMARK(CellToPointAvg, BenchCellToPointAvg); VTKM_MAKE_BENCHMARK(CellToPointAvgDynamic, BenchCellToPointAvgDynamic); template struct BenchPointToCellAvg { std::vector input; vtkm::cont::ArrayHandle InputHandle; std::size_t DomainSize; VTKM_CONT BenchPointToCellAvg() { NumberGenerator generator(static_cast(1.0), static_cast(100.0)); this->DomainSize = (CUBE_SIZE) * (CUBE_SIZE) * (CUBE_SIZE); this->input.resize(DomainSize); for (std::size_t i = 0; i < DomainSize; ++i) { this->input[i] = generator.next(); } this->InputHandle = vtkm::cont::make_ArrayHandle(this->input); } VTKM_CONT vtkm::Float64 operator()() { vtkm::cont::CellSetStructured<3> cellSet; cellSet.SetPointDimensions(vtkm::Id3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE)); vtkm::cont::ArrayHandle result; Timer timer; vtkm::worklet::DispatcherMapTopology dispatcher; dispatcher.SetDevice(DeviceAdapterTag()); dispatcher.Invoke(this->InputHandle, cellSet, result); return timer.GetElapsedTime(); } virtual std::string Type() const { return std::string("Static"); } VTKM_CONT std::string Description() const { std::stringstream description; description << "Computing Point To Cell Average " << "[" << this->Type() << "] " << "with a domain size of: " << this->DomainSize; return description.str(); } }; template struct BenchPointToCellAvgDynamic : public BenchPointToCellAvg { VTKM_CONT vtkm::Float64 operator()() { vtkm::cont::CellSetStructured<3> cellSet; cellSet.SetPointDimensions(vtkm::Id3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE)); ValueVariantHandle dinput(this->InputHandle); vtkm::cont::ArrayHandle result; Timer timer; vtkm::worklet::DispatcherMapTopology dispatcher; dispatcher.SetDevice(DeviceAdapterTag()); dispatcher.Invoke(dinput, cellSet, result); return timer.GetElapsedTime(); } virtual std::string Type() const { return std::string("Dynamic"); } }; VTKM_MAKE_BENCHMARK(PointToCellAvg, BenchPointToCellAvg); VTKM_MAKE_BENCHMARK(PointToCellAvgDynamic, BenchPointToCellAvgDynamic); template struct BenchClassification { std::vector input; vtkm::cont::ArrayHandle InputHandle; Value IsoValue; size_t DomainSize; VTKM_CONT BenchClassification() { NumberGenerator generator(static_cast(1.0), static_cast(100.0)); this->DomainSize = (CUBE_SIZE) * (CUBE_SIZE) * (CUBE_SIZE); this->input.resize(DomainSize); for (std::size_t i = 0; i < DomainSize; ++i) { this->input[i] = generator.next(); } this->InputHandle = vtkm::cont::make_ArrayHandle(this->input); this->IsoValue = generator.next(); } VTKM_CONT vtkm::Float64 operator()() { vtkm::cont::CellSetStructured<3> cellSet; cellSet.SetPointDimensions(vtkm::Id3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE)); vtkm::cont::ArrayHandle result; ValueVariantHandle dinput(this->InputHandle); Timer timer; Classification worklet(this->IsoValue); vtkm::worklet::DispatcherMapTopology> dispatcher(worklet); dispatcher.SetDevice(DeviceAdapterTag()); dispatcher.Invoke(dinput, cellSet, result); return timer.GetElapsedTime(); } virtual std::string Type() const { return std::string("Static"); } VTKM_CONT std::string Description() const { std::stringstream description; description << "Computing Marching Cubes Classification " << "[" << this->Type() << "] " << "with a domain size of: " << this->DomainSize; return description.str(); } }; template struct BenchClassificationDynamic : public BenchClassification { VTKM_CONT vtkm::Float64 operator()() { vtkm::cont::CellSetStructured<3> cellSet; cellSet.SetPointDimensions(vtkm::Id3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE)); vtkm::cont::ArrayHandle result; Timer timer; Classification worklet(this->IsoValue); vtkm::worklet::DispatcherMapTopology> dispatcher(worklet); dispatcher.SetDevice(DeviceAdapterTag()); dispatcher.Invoke(this->InputHandle, cellSet, result); return timer.GetElapsedTime(); } virtual std::string Type() const { return std::string("Dynamic"); } }; VTKM_MAKE_BENCHMARK(Classification, BenchClassification); VTKM_MAKE_BENCHMARK(ClassificationDynamic, BenchClassificationDynamic); public: static VTKM_CONT int Run(int benchmarks) { std::cout << DIVIDER << "\nRunning Topology Algorithm benchmarks\n"; if (benchmarks & CELL_TO_POINT) { std::cout << DIVIDER << "\nBenchmarking Cell To Point Average\n"; VTKM_RUN_BENCHMARK(CellToPointAvg, ValueTypes()); VTKM_RUN_BENCHMARK(CellToPointAvgDynamic, ValueTypes()); } if (benchmarks & POINT_TO_CELL) { std::cout << DIVIDER << "\nBenchmarking Point to Cell Average\n"; VTKM_RUN_BENCHMARK(PointToCellAvg, ValueTypes()); VTKM_RUN_BENCHMARK(PointToCellAvgDynamic, ValueTypes()); } if (benchmarks & MC_CLASSIFY) { std::cout << DIVIDER << "\nBenchmarking Hex/Voxel MC Classification\n"; VTKM_RUN_BENCHMARK(Classification, ValueTypes()); VTKM_RUN_BENCHMARK(ClassificationDynamic, ValueTypes()); } return 0; } }; #undef ARRAY_SIZE } } // namespace vtkm::benchmarking int main(int argc, char* argv[]) { vtkm::cont::InitLogging(argc, argv); int benchmarks = 0; if (argc < 2) { benchmarks = vtkm::benchmarking::ALL; } else { for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; std::transform(arg.begin(), arg.end(), arg.begin(), [](char c) { return static_cast(std::tolower(static_cast(c))); }); if (arg == "celltopoint") { benchmarks |= vtkm::benchmarking::CELL_TO_POINT; } else if (arg == "pointtocell") { benchmarks |= vtkm::benchmarking::POINT_TO_CELL; } else if (arg == "classify") { benchmarks |= vtkm::benchmarking::MC_CLASSIFY; } else { std::cout << "Unrecognized benchmark: " << argv[i] << std::endl; return 1; } } } //now actually execute the benchmarks using Device = VTKM_DEFAULT_DEVICE_ADAPTER_TAG; auto tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker(); tracker.ForceDevice(Device{}); return vtkm::benchmarking::BenchmarkTopologyAlgorithms::Run(benchmarks); }