vtk-m/benchmarking/BenchmarkFieldAlgorithms.cxx
Allison Vacanti 5db762ee71 Refactor topology mappings to clarify meaning.
The `From` and `To` nomenclature for topology mapping has been confusing for
both users and developers, especially at lower levels where the intention of
mapping attributes from one element to another is easily conflated with the
concept of mapping indices (which maps in the exact opposite direction).

These identifiers have been renamed to `VisitTopology` and `IncidentTopology`
to clarify the direction of the mapping. The order in which these template
parameters are specified for `WorkletMapTopology` have also been reversed,
since eventually there may be more than one `IncidentTopology`, and having
`IncidentTopology` at the end will allow us to replace it with a variadic
template parameter pack in the future.

Other implementation details supporting these worklets, include `Fetch` tags,
`Connectivity` classes, and methods on the various `CellSet` classes (such as
`PrepareForInput` have also reversed their template arguments. These will need
to be cautiously updated.

The convenience implementations of `WorkletMapTopology` have been renamed for
clarity as follows:

```
WorkletMapPointToCell --> WorkletVisitCellsWithPoints
WorkletMapCellToPoint --> WorkletVisitPointsWithCells
```

The `ControlSignature` tags have been renamed as follows:

```
FieldInTo --> FieldInVisit
FieldInFrom --> FieldInMap
FromCount --> IncidentElementCount
FromIndices --> IncidentElementIndices
```
2019-08-06 11:27:26 -04:00

1182 lines
36 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 <vtkm/Math.h>
#include <vtkm/VectorAnalysis.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleMultiplexer.h>
#include <vtkm/cont/ArrayHandleVirtual.h>
#include <vtkm/cont/CellSetStructured.h>
#include <vtkm/cont/ImplicitFunctionHandle.h>
#include <vtkm/cont/Initialize.h>
#include <vtkm/cont/Invoker.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletMapTopology.h>
#include "Benchmarker.h"
#include <vtkm/cont/testing/Testing.h>
#include <cctype>
#include <random>
#include <string>
namespace vtkm
{
namespace benchmarking
{
#define ARRAY_SIZE (1 << 22)
#define CUBE_SIZE 256
static const std::string DIVIDER(40, '-');
enum BenchmarkName
{
BLACK_SCHOLES = 1,
MATH = 1 << 1,
FUSED_MATH = 1 << 2,
INTERPOLATE_FIELD = 1 << 3,
IMPLICIT_FUNCTION = 1 << 4,
ALL = BLACK_SCHOLES | MATH | FUSED_MATH | INTERPOLATE_FIELD | IMPLICIT_FUNCTION
};
template <typename T>
class BlackScholes : public vtkm::worklet::WorkletMapField
{
T Riskfree;
T Volatility;
public:
using ControlSignature = void(FieldIn, FieldIn, FieldIn, FieldOut, FieldOut);
using ExecutionSignature = void(_1, _2, _3, _4, _5);
BlackScholes(T risk, T volatility)
: Riskfree(risk)
, Volatility(volatility)
{
}
VTKM_EXEC
T CumulativeNormalDistribution(T d) const
{
const vtkm::Float32 A1 = 0.31938153f;
const vtkm::Float32 A2 = -0.356563782f;
const vtkm::Float32 A3 = 1.781477937f;
const vtkm::Float32 A4 = -1.821255978f;
const vtkm::Float32 A5 = 1.330274429f;
const vtkm::Float32 RSQRT2PI = 0.39894228040143267793994605993438f;
const vtkm::Float32 df = static_cast<vtkm::Float32>(d);
const vtkm::Float32 K = 1.0f / (1.0f + 0.2316419f * vtkm::Abs(df));
vtkm::Float32 cnd =
RSQRT2PI * vtkm::Exp(-0.5f * df * df) * (K * (A1 + K * (A2 + K * (A3 + K * (A4 + K * A5)))));
if (df > 0.0f)
{
cnd = 1.0f - cnd;
}
return static_cast<T>(cnd);
}
template <typename U, typename V, typename W>
VTKM_EXEC void operator()(const U& sp, const V& os, const W& oy, T& callResult, T& putResult)
const
{
const T stockPrice = static_cast<T>(sp);
const T optionStrike = static_cast<T>(os);
const T optionYears = static_cast<T>(oy);
// Black-Scholes formula for both call and put
const T sqrtYears = vtkm::Sqrt(optionYears);
const T volMultSqY = this->Volatility * sqrtYears;
const T d1 = (vtkm::Log(stockPrice / optionStrike) +
(this->Riskfree + 0.5f * Volatility * Volatility) * optionYears) /
(volMultSqY);
const T d2 = d1 - volMultSqY;
const T CNDD1 = CumulativeNormalDistribution(d1);
const T CNDD2 = CumulativeNormalDistribution(d2);
//Calculate Call and Put simultaneously
T expRT = vtkm::Exp(-this->Riskfree * optionYears);
callResult = stockPrice * CNDD1 - optionStrike * expRT * CNDD2;
putResult = optionStrike * expRT * (1.0f - CNDD2) - stockPrice * (1.0f - CNDD1);
}
};
class Mag : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
template <typename T, typename U>
VTKM_EXEC void operator()(const vtkm::Vec<T, 3>& vec, U& result) const
{
result = static_cast<U>(vtkm::Magnitude(vec));
}
};
class Square : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
template <typename T, typename U>
VTKM_EXEC void operator()(T input, U& output) const
{
output = static_cast<U>(input * input);
}
};
class Sin : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
template <typename T, typename U>
VTKM_EXEC void operator()(T input, U& output) const
{
output = static_cast<U>(vtkm::Sin(input));
}
};
class Cos : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
template <typename T, typename U>
VTKM_EXEC void operator()(T input, U& output) const
{
output = static_cast<U>(vtkm::Cos(input));
}
};
class FusedMath : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
template <typename T>
VTKM_EXEC void operator()(const vtkm::Vec<T, 3>& vec, T& result) const
{
const T m = vtkm::Magnitude(vec);
result = vtkm::Cos(vtkm::Sin(m) * vtkm::Sin(m));
}
template <typename T, typename U>
VTKM_EXEC void operator()(const vtkm::Vec<T, 3>&, U&) const
{
this->RaiseError("Mixed types unsupported.");
}
};
class GenerateEdges : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
using ControlSignature = void(CellSetIn cellset, WholeArrayOut edgeIds);
using ExecutionSignature = void(PointIndices, ThreadIndices, _2);
using InputDomain = _1;
template <typename ConnectivityInVec, typename ThreadIndicesType, typename IdPairTableType>
VTKM_EXEC void operator()(const ConnectivityInVec& connectivity,
const ThreadIndicesType threadIndices,
const IdPairTableType& edgeIds) const
{
const vtkm::Id writeOffset = (threadIndices.GetInputIndex() * 12);
const vtkm::IdComponent edgeTable[24] = { 0, 1, 1, 2, 3, 2, 0, 3, 4, 5, 5, 6,
7, 6, 4, 7, 0, 4, 1, 5, 2, 6, 3, 7 };
for (vtkm::Id i = 0; i < 12; ++i)
{
const vtkm::Id offset = (i * 2);
const vtkm::Id2 edge(connectivity[edgeTable[offset]], connectivity[edgeTable[offset + 1]]);
edgeIds.Set(writeOffset + i, edge);
}
}
};
class InterpolateField : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn interpolation_ids,
FieldIn interpolation_weights,
WholeArrayIn inputField,
FieldOut output);
using ExecutionSignature = void(_1, _2, _3, _4);
using InputDomain = _1;
template <typename WeightType, typename T, typename S, typename D>
VTKM_EXEC void operator()(const vtkm::Id2& low_high,
const WeightType& weight,
const vtkm::exec::ExecutionWholeArrayConst<T, S, D>& inPortal,
T& result) const
{
//fetch the low / high values from inPortal
result = vtkm::Lerp(inPortal.Get(low_high[0]), inPortal.Get(low_high[1]), weight);
}
template <typename WeightType, typename T, typename S, typename D, typename U>
VTKM_EXEC void operator()(const vtkm::Id2&,
const WeightType&,
const vtkm::exec::ExecutionWholeArrayConst<T, S, D>&,
U&) const
{
//the inPortal and result need to be the same type so this version only
//exists to generate code when using dynamic arrays
this->RaiseError("Mixed types unsupported.");
}
};
template <typename ImplicitFunction>
class EvaluateImplicitFunction : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
EvaluateImplicitFunction(const ImplicitFunction* function)
: Function(function)
{
}
template <typename VecType, typename ScalarType>
VTKM_EXEC void operator()(const VecType& point, ScalarType& val) const
{
val = this->Function->Value(point);
}
private:
const ImplicitFunction* Function;
};
template <typename T1, typename T2>
class Evaluate2ImplicitFunctions : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2);
Evaluate2ImplicitFunctions(const T1* f1, const T2* f2)
: Function1(f1)
, Function2(f2)
{
}
template <typename VecType, typename ScalarType>
VTKM_EXEC void operator()(const VecType& point, ScalarType& val) const
{
val = this->Function1->Value(point) + this->Function2->Value(point);
}
private:
const T1* Function1;
const T2* Function2;
};
struct PassThroughFunctor
{
template <typename T>
VTKM_EXEC_CONT T operator()(const T& x) const
{
return x;
}
};
template <typename ArrayHandleType>
using ArrayHandlePassThrough =
vtkm::cont::ArrayHandleTransform<ArrayHandleType, PassThroughFunctor, PassThroughFunctor>;
template <typename ValueType, vtkm::IdComponent>
struct JunkArrayHandle : vtkm::cont::ArrayHandle<ValueType>
{
};
template <typename ArrayHandleType>
using BMArrayHandleMultiplexer =
vtkm::cont::ArrayHandleMultiplexer<ArrayHandleType,
JunkArrayHandle<typename ArrayHandleType::ValueType, 0>,
JunkArrayHandle<typename ArrayHandleType::ValueType, 1>,
JunkArrayHandle<typename ArrayHandleType::ValueType, 2>,
JunkArrayHandle<typename ArrayHandleType::ValueType, 3>,
JunkArrayHandle<typename ArrayHandleType::ValueType, 4>,
JunkArrayHandle<typename ArrayHandleType::ValueType, 5>,
JunkArrayHandle<typename ArrayHandleType::ValueType, 6>,
JunkArrayHandle<typename ArrayHandleType::ValueType, 7>,
JunkArrayHandle<typename ArrayHandleType::ValueType, 8>,
JunkArrayHandle<typename ArrayHandleType::ValueType, 9>,
ArrayHandlePassThrough<ArrayHandleType>>;
template <typename ArrayHandleType>
BMArrayHandleMultiplexer<ArrayHandleType> make_ArrayHandleMultiplexer0(const ArrayHandleType& array)
{
VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
return BMArrayHandleMultiplexer<ArrayHandleType>(array);
}
template <typename ArrayHandleType>
BMArrayHandleMultiplexer<ArrayHandleType> make_ArrayHandleMultiplexerN(const ArrayHandleType& array)
{
VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
return BMArrayHandleMultiplexer<ArrayHandleType>(ArrayHandlePassThrough<ArrayHandleType>(array));
}
struct ValueTypes : vtkm::ListTagBase<vtkm::Float32, vtkm::Float64>
{
};
struct InterpValueTypes : vtkm::ListTagBase<vtkm::Float32, vtkm::Vec3f_32>
{
};
/// This class runs a series of micro-benchmarks to measure
/// performance of different field operations
class BenchmarkFieldAlgorithms
{
using StorageTag = vtkm::cont::StorageTagBasic;
using Timer = vtkm::cont::Timer;
private:
template <typename Value, typename DeviceAdapter>
struct BenchBlackScholes
{
using ValueArrayHandle = vtkm::cont::ArrayHandle<Value, StorageTag>;
ValueArrayHandle StockPrice;
ValueArrayHandle OptionStrike;
ValueArrayHandle OptionYears;
std::vector<Value> price;
std::vector<Value> strike;
std::vector<Value> years;
VTKM_CONT
BenchBlackScholes()
{
std::mt19937 rng;
std::uniform_real_distribution<Value> price_range(Value(5.0f), Value(30.0f));
std::uniform_real_distribution<Value> strike_range(Value(1.0f), Value(100.0f));
std::uniform_real_distribution<Value> year_range(Value(0.25f), Value(10.0f));
this->price.resize(ARRAY_SIZE);
this->strike.resize(ARRAY_SIZE);
this->years.resize(ARRAY_SIZE);
for (std::size_t i = 0; i < ARRAY_SIZE; ++i)
{
this->price[i] = price_range(rng);
this->strike[i] = strike_range(rng);
this->years[i] = year_range(rng);
}
this->StockPrice = vtkm::cont::make_ArrayHandle(this->price);
this->OptionStrike = vtkm::cont::make_ArrayHandle(this->strike);
this->OptionYears = vtkm::cont::make_ArrayHandle(this->years);
}
template <typename StockPriceType, typename OptionStrikeType, typename OptionYearsType>
VTKM_CONT static vtkm::Float64 Run(const StockPriceType& stockPrice,
const OptionStrikeType& optionStrike,
const OptionYearsType& optionYears)
{
vtkm::cont::ArrayHandle<Value> callResultHandle, putResultHandle;
const Value RISKFREE = 0.02f;
const Value VOLATILITY = 0.30f;
Timer timer{ DeviceAdapter() };
timer.Start();
BlackScholes<Value> worklet(RISKFREE, VOLATILITY);
vtkm::worklet::DispatcherMapField<BlackScholes<Value>> dispatcher(worklet);
dispatcher.Invoke(stockPrice, optionStrike, optionYears, callResultHandle, putResultHandle);
return timer.GetElapsedTime();
}
VTKM_CONT
vtkm::Float64 operator()()
{
return this->Run(this->StockPrice, this->OptionStrike, this->OptionYears);
}
virtual std::string Type() const { return std::string("Static"); }
VTKM_CONT
std::string Description() const
{
std::stringstream description;
description << "BlackScholes "
<< "[" << this->Type() << "] "
<< " with a domain size of: " << ARRAY_SIZE;
return description.str();
}
};
template <typename Value, typename DeviceAdapter>
struct BenchBlackScholesDynamic : public BenchBlackScholes<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
return this->Run(vtkm::cont::make_ArrayHandleVirtual(this->StockPrice),
vtkm::cont::make_ArrayHandleVirtual(this->OptionStrike),
vtkm::cont::make_ArrayHandleVirtual(this->OptionYears));
}
virtual std::string Type() const { return std::string("Dynamic"); }
};
template <typename Value, typename DeviceAdapter>
struct BenchBlackScholesMultiplexer0 : public BenchBlackScholes<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
return this->Run(make_ArrayHandleMultiplexer0(this->StockPrice),
make_ArrayHandleMultiplexer0(this->OptionStrike),
make_ArrayHandleMultiplexer0(this->OptionYears));
}
virtual std::string Type() const
{
std::stringstream desc;
desc << "Multiplexer-"
<< make_ArrayHandleMultiplexer0(this->StockPrice)
.GetStorage()
.GetArrayHandleVariant()
.GetIndex();
return desc.str();
}
};
template <typename Value, typename DeviceAdapter>
struct BenchBlackScholesMultiplexerN : public BenchBlackScholes<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
return this->Run(make_ArrayHandleMultiplexerN(this->StockPrice),
make_ArrayHandleMultiplexerN(this->OptionStrike),
make_ArrayHandleMultiplexerN(this->OptionYears));
}
virtual std::string Type() const
{
std::stringstream desc;
desc << "Multiplexer-"
<< make_ArrayHandleMultiplexerN(this->StockPrice)
.GetStorage()
.GetArrayHandleVariant()
.GetIndex();
return desc.str();
}
};
VTKM_MAKE_BENCHMARK(BlackScholes, BenchBlackScholes);
VTKM_MAKE_BENCHMARK(BlackScholesDynamic, BenchBlackScholesDynamic);
VTKM_MAKE_BENCHMARK(BlackScholesMultiplexer0, BenchBlackScholesMultiplexer0);
VTKM_MAKE_BENCHMARK(BlackScholesMultiplexerN, BenchBlackScholesMultiplexerN);
template <typename Value, typename DeviceAdapter>
struct BenchMath
{
std::vector<vtkm::Vec<Value, 3>> input;
vtkm::cont::ArrayHandle<vtkm::Vec<Value, 3>, StorageTag> InputHandle;
VTKM_CONT
BenchMath()
{
std::mt19937 rng;
std::uniform_real_distribution<Value> range;
this->input.resize(ARRAY_SIZE);
for (std::size_t i = 0; i < ARRAY_SIZE; ++i)
{
this->input[i] = vtkm::Vec<Value, 3>(range(rng), range(rng), range(rng));
}
this->InputHandle = vtkm::cont::make_ArrayHandle(this->input);
}
VTKM_CONT
vtkm::Float64 operator()()
{
vtkm::cont::ArrayHandle<Value> tempHandle1;
vtkm::cont::ArrayHandle<Value> tempHandle2;
Timer timer{ DeviceAdapter() };
timer.Start();
vtkm::cont::Invoker invoke(DeviceAdapter{});
invoke(Mag{}, this->InputHandle, tempHandle1);
invoke(Sin{}, tempHandle1, tempHandle2);
invoke(Square{}, tempHandle2, tempHandle1);
invoke(Cos{}, tempHandle1, tempHandle2);
return timer.GetElapsedTime();
}
virtual std::string Type() const { return std::string("Static"); }
VTKM_CONT
std::string Description() const
{
std::stringstream description;
description << "Magnitude -> Sine -> Square -> Cosine "
<< "[" << this->Type() << "] "
<< "with a domain size of: " << ARRAY_SIZE;
return description.str();
}
};
template <typename Value, typename DeviceAdapter>
struct BenchMathDynamic : public BenchMath<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
vtkm::cont::ArrayHandle<Value> temp1;
vtkm::cont::ArrayHandle<Value> temp2;
auto dinput = vtkm::cont::make_ArrayHandleVirtual(this->InputHandle);
auto dtemp1 = vtkm::cont::make_ArrayHandleVirtual(temp1);
auto dtemp2 = vtkm::cont::make_ArrayHandleVirtual(temp2);
Timer timer{ DeviceAdapter() };
timer.Start();
vtkm::cont::Invoker invoke(DeviceAdapter{});
invoke(Mag{}, dinput, dtemp1);
invoke(Sin{}, dtemp1, dtemp2);
invoke(Square{}, dtemp2, dtemp1);
invoke(Cos{}, dtemp1, dtemp2);
return timer.GetElapsedTime();
}
virtual std::string Type() const { return std::string("Dynamic"); }
};
template <typename Value, typename DeviceAdapter>
struct BenchMathMultiplexer0 : public BenchMath<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
vtkm::cont::ArrayHandle<Value> temp1;
vtkm::cont::ArrayHandle<Value> temp2;
auto mInput = make_ArrayHandleMultiplexer0(this->InputHandle);
auto mTemp1 = make_ArrayHandleMultiplexer0(temp1);
auto mTemp2 = make_ArrayHandleMultiplexer0(temp2);
Timer timer{ DeviceAdapter() };
timer.Start();
vtkm::cont::Invoker invoke(DeviceAdapter{});
invoke(Mag{}, mInput, mTemp1);
invoke(Sin{}, mTemp1, mTemp2);
invoke(Square{}, mTemp2, mTemp1);
invoke(Cos{}, mTemp1, mTemp2);
return timer.GetElapsedTime();
}
virtual std::string Type() const
{
std::stringstream desc;
desc << "Multiplexer-"
<< make_ArrayHandleMultiplexer0(this->InputHandle)
.GetStorage()
.GetArrayHandleVariant()
.GetIndex();
return desc.str();
}
};
template <typename Value, typename DeviceAdapter>
struct BenchMathMultiplexerN : public BenchMath<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
vtkm::cont::ArrayHandle<Value> temp1;
vtkm::cont::ArrayHandle<Value> temp2;
auto mInput = make_ArrayHandleMultiplexerN(this->InputHandle);
auto mTemp1 = make_ArrayHandleMultiplexerN(temp1);
auto mTemp2 = make_ArrayHandleMultiplexerN(temp2);
Timer timer{ DeviceAdapter() };
timer.Start();
vtkm::cont::Invoker invoke(DeviceAdapter{});
invoke(Mag{}, mInput, mTemp1);
invoke(Sin{}, mTemp1, mTemp2);
invoke(Square{}, mTemp2, mTemp1);
invoke(Cos{}, mTemp1, mTemp2);
return timer.GetElapsedTime();
}
virtual std::string Type() const
{
std::stringstream desc;
desc << "Multiplexer-"
<< make_ArrayHandleMultiplexerN(this->InputHandle)
.GetStorage()
.GetArrayHandleVariant()
.GetIndex();
return desc.str();
}
};
VTKM_MAKE_BENCHMARK(Math, BenchMath);
VTKM_MAKE_BENCHMARK(MathDynamic, BenchMathDynamic);
VTKM_MAKE_BENCHMARK(MathMultiplexer0, BenchMathMultiplexer0);
VTKM_MAKE_BENCHMARK(MathMultiplexerN, BenchMathMultiplexerN);
template <typename Value, typename DeviceAdapter>
struct BenchFusedMath
{
std::vector<vtkm::Vec<Value, 3>> input;
vtkm::cont::ArrayHandle<vtkm::Vec<Value, 3>, StorageTag> InputHandle;
VTKM_CONT
BenchFusedMath()
{
std::mt19937 rng;
std::uniform_real_distribution<Value> range;
this->input.resize(ARRAY_SIZE);
for (std::size_t i = 0; i < ARRAY_SIZE; ++i)
{
this->input[i] = vtkm::Vec<Value, 3>(range(rng), range(rng), range(rng));
}
this->InputHandle = vtkm::cont::make_ArrayHandle(this->input);
}
template <typename InputHandleType>
VTKM_CONT static vtkm::Float64 Run(const InputHandleType& inputHandle)
{
vtkm::cont::ArrayHandle<Value, StorageTag> result;
Timer timer{ DeviceAdapter() };
timer.Start();
vtkm::worklet::DispatcherMapField<FusedMath> dispatcher;
dispatcher.Invoke(inputHandle, result);
return timer.GetElapsedTime();
}
VTKM_CONT
vtkm::Float64 operator()() { return this->Run(this->InputHandle); }
virtual std::string Type() const { return std::string("Static"); }
VTKM_CONT
std::string Description() const
{
std::stringstream description;
description << "Fused Magnitude -> Sine -> Square -> Cosine "
<< "[" << this->Type() << "] "
<< "with a domain size of: " << ARRAY_SIZE;
return description.str();
}
};
template <typename Value, typename DeviceAdapter>
struct BenchFusedMathDynamic : public BenchFusedMath<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
return this->Run(vtkm::cont::make_ArrayHandleVirtual(this->InputHandle));
}
virtual std::string Type() const { return std::string("Dynamic"); }
};
template <typename Value, typename DeviceAdapter>
struct BenchFusedMathMultiplexer0 : public BenchFusedMath<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
return this->Run(make_ArrayHandleMultiplexer0(this->InputHandle));
}
virtual std::string Type() const
{
std::stringstream desc;
desc << "Multiplexer-"
<< make_ArrayHandleMultiplexer0(this->InputHandle)
.GetStorage()
.GetArrayHandleVariant()
.GetIndex();
return desc.str();
}
};
template <typename Value, typename DeviceAdapter>
struct BenchFusedMathMultiplexerN : public BenchFusedMath<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
return this->Run(make_ArrayHandleMultiplexerN(this->InputHandle));
}
virtual std::string Type() const
{
std::stringstream desc;
desc << "Multiplexer-"
<< make_ArrayHandleMultiplexerN(this->InputHandle)
.GetStorage()
.GetArrayHandleVariant()
.GetIndex();
return desc.str();
}
};
VTKM_MAKE_BENCHMARK(FusedMath, BenchFusedMath);
VTKM_MAKE_BENCHMARK(FusedMathDynamic, BenchFusedMathDynamic);
VTKM_MAKE_BENCHMARK(FusedMathMultiplexer0, BenchFusedMathMultiplexer0);
VTKM_MAKE_BENCHMARK(FusedMathMultiplexerN, BenchFusedMathMultiplexerN);
template <typename Value, typename DeviceAdapter>
struct BenchEdgeInterp
{
std::vector<vtkm::Float32> weight;
std::vector<Value> field;
vtkm::cont::ArrayHandle<vtkm::Float32, StorageTag> WeightHandle;
vtkm::cont::ArrayHandle<Value, StorageTag> FieldHandle;
vtkm::cont::ArrayHandle<vtkm::Id2, StorageTag> EdgePairHandle;
VTKM_CONT
BenchEdgeInterp()
{
using CT = typename vtkm::VecTraits<Value>::ComponentType;
std::mt19937 rng;
std::uniform_real_distribution<vtkm::Float32> weight_range(0.0f, 1.0f);
std::uniform_real_distribution<CT> field_range;
//basically the core challenge is to generate an array whose
//indexing pattern matches that of a edge based algorithm.
//
//So for this kind of problem we generate the 12 edges of each
//cell and place them into array.
//
vtkm::cont::CellSetStructured<3> cellSet;
cellSet.SetPointDimensions(vtkm::Id3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE));
const vtkm::Id numberOfEdges = cellSet.GetNumberOfCells() * 12;
const std::size_t esize = static_cast<std::size_t>(numberOfEdges);
const std::size_t psize = static_cast<std::size_t>(cellSet.GetNumberOfPoints());
this->EdgePairHandle.Allocate(numberOfEdges);
vtkm::worklet::DispatcherMapTopology<GenerateEdges> dispatcher;
dispatcher.Invoke(cellSet, this->EdgePairHandle);
this->weight.resize(esize);
for (std::size_t i = 0; i < esize; ++i)
{
this->weight[i] = weight_range(rng);
}
this->field.resize(psize);
for (std::size_t i = 0; i < psize; ++i)
{
this->field[i] = Value(field_range(rng));
}
this->FieldHandle = vtkm::cont::make_ArrayHandle(this->field);
this->WeightHandle = vtkm::cont::make_ArrayHandle(this->weight);
}
VTKM_CONT
vtkm::Float64 operator()()
{
vtkm::cont::ArrayHandle<Value> result;
Timer timer{ DeviceAdapter() };
timer.Start();
vtkm::worklet::DispatcherMapField<InterpolateField> dispatcher;
dispatcher.Invoke(this->EdgePairHandle, this->WeightHandle, this->FieldHandle, result);
return timer.GetElapsedTime();
}
virtual std::string Type() const { return std::string("Static"); }
VTKM_CONT
std::string Description() const
{
std::stringstream description;
const std::size_t size = (CUBE_SIZE - 1) * (CUBE_SIZE - 1) * (CUBE_SIZE - 1) * 12;
description << "Edge Interpolation "
<< "[" << this->Type() << "] "
<< "with a domain size of: " << size;
return description.str();
}
};
template <typename Value, typename DeviceAdapter>
struct BenchEdgeInterpDynamic : public BenchEdgeInterp<Value, DeviceAdapter>
{
VTKM_CONT
vtkm::Float64 operator()()
{
auto dfield = vtkm::cont::make_ArrayHandleVirtual(this->FieldHandle);
auto dweight = vtkm::cont::make_ArrayHandleVirtual(this->WeightHandle);
auto dedges = vtkm::cont::make_ArrayHandleVirtual(this->EdgePairHandle);
vtkm::cont::ArrayHandle<Value> result;
Timer timer{ DeviceAdapter() };
timer.Start();
vtkm::worklet::DispatcherMapField<InterpolateField> dispatcher;
dispatcher.Invoke(dedges, dweight, dfield, result);
return timer.GetElapsedTime();
}
virtual std::string Type() const { return std::string("Dynamic"); }
};
VTKM_MAKE_BENCHMARK(EdgeInterp, BenchEdgeInterp);
VTKM_MAKE_BENCHMARK(EdgeInterpDynamic, BenchEdgeInterpDynamic);
struct ImplicitFunctionBenchData
{
vtkm::cont::ArrayHandle<vtkm::Vec3f> Points;
vtkm::cont::ArrayHandle<vtkm::FloatDefault> Result;
vtkm::Sphere Sphere1, Sphere2;
};
static ImplicitFunctionBenchData MakeImplicitFunctionBenchData()
{
vtkm::Id count = ARRAY_SIZE;
vtkm::FloatDefault bounds[6] = { -2.0f, 2.0f, -2.0f, 2.0f, -2.0f, 2.0f };
ImplicitFunctionBenchData data;
data.Points.Allocate(count);
data.Result.Allocate(count);
std::default_random_engine rangen;
std::uniform_real_distribution<vtkm::FloatDefault> distx(bounds[0], bounds[1]);
std::uniform_real_distribution<vtkm::FloatDefault> disty(bounds[2], bounds[3]);
std::uniform_real_distribution<vtkm::FloatDefault> distz(bounds[4], bounds[5]);
auto portal = data.Points.GetPortalControl();
for (vtkm::Id i = 0; i < count; ++i)
{
portal.Set(i, vtkm::make_Vec(distx(rangen), disty(rangen), distz(rangen)));
}
data.Sphere1 = vtkm::Sphere({ 0.22f, 0.33f, 0.44f }, 0.55f);
data.Sphere2 = vtkm::Sphere({ 0.22f, 0.33f, 0.11f }, 0.77f);
return data;
}
template <typename Value, typename DeviceAdapter>
struct BenchImplicitFunction
{
BenchImplicitFunction()
: Internal(MakeImplicitFunctionBenchData())
{
}
VTKM_CONT
vtkm::Float64 operator()()
{
using EvalWorklet = EvaluateImplicitFunction<vtkm::Sphere>;
using EvalDispatcher = vtkm::worklet::DispatcherMapField<EvalWorklet>;
auto handle = vtkm::cont::make_ImplicitFunctionHandle(Internal.Sphere1);
auto function = static_cast<const vtkm::Sphere*>(handle.PrepareForExecution(DeviceAdapter()));
EvalWorklet eval(function);
Timer timer{ DeviceAdapter() };
timer.Start();
EvalDispatcher dispatcher(eval);
dispatcher.Invoke(this->Internal.Points, this->Internal.Result);
return timer.GetElapsedTime();
}
VTKM_CONT
std::string Description() const
{
std::stringstream description;
description << "Implicit Function (vtkm::Sphere) on " << Internal.Points.GetNumberOfValues()
<< " points";
return description.str();
}
ImplicitFunctionBenchData Internal;
};
template <typename Value, typename DeviceAdapter>
struct BenchVirtualImplicitFunction
{
BenchVirtualImplicitFunction()
: Internal(MakeImplicitFunctionBenchData())
{
}
VTKM_CONT
vtkm::Float64 operator()()
{
using EvalWorklet = EvaluateImplicitFunction<vtkm::ImplicitFunction>;
using EvalDispatcher = vtkm::worklet::DispatcherMapField<EvalWorklet>;
auto sphere = vtkm::cont::make_ImplicitFunctionHandle(Internal.Sphere1);
EvalWorklet eval(sphere.PrepareForExecution(DeviceAdapter()));
Timer timer{ DeviceAdapter() };
timer.Start();
EvalDispatcher dispatcher(eval);
dispatcher.Invoke(this->Internal.Points, this->Internal.Result);
return timer.GetElapsedTime();
}
VTKM_CONT
std::string Description() const
{
std::stringstream description;
description << "Implicit Function (VirtualImplicitFunction) on "
<< Internal.Points.GetNumberOfValues() << " points";
return description.str();
}
ImplicitFunctionBenchData Internal;
};
template <typename Value, typename DeviceAdapter>
struct Bench2ImplicitFunctions
{
Bench2ImplicitFunctions()
: Internal(MakeImplicitFunctionBenchData())
{
}
VTKM_CONT
vtkm::Float64 operator()()
{
using EvalWorklet = Evaluate2ImplicitFunctions<vtkm::Sphere, vtkm::Sphere>;
using EvalDispatcher = vtkm::worklet::DispatcherMapField<EvalWorklet>;
auto h1 = vtkm::cont::make_ImplicitFunctionHandle(Internal.Sphere1);
auto h2 = vtkm::cont::make_ImplicitFunctionHandle(Internal.Sphere2);
auto f1 = static_cast<const vtkm::Sphere*>(h1.PrepareForExecution(DeviceAdapter()));
auto f2 = static_cast<const vtkm::Sphere*>(h2.PrepareForExecution(DeviceAdapter()));
EvalWorklet eval(f1, f2);
Timer timer{ DeviceAdapter() };
timer.Start();
EvalDispatcher dispatcher(eval);
dispatcher.Invoke(this->Internal.Points, this->Internal.Result);
return timer.GetElapsedTime();
}
VTKM_CONT
std::string Description() const
{
std::stringstream description;
description << "Implicit Function 2x(vtkm::Sphere) on " << Internal.Points.GetNumberOfValues()
<< " points";
return description.str();
}
ImplicitFunctionBenchData Internal;
};
template <typename Value, typename DeviceAdapter>
struct Bench2VirtualImplicitFunctions
{
Bench2VirtualImplicitFunctions()
: Internal(MakeImplicitFunctionBenchData())
{
}
VTKM_CONT
vtkm::Float64 operator()()
{
using EvalWorklet =
Evaluate2ImplicitFunctions<vtkm::ImplicitFunction, vtkm::ImplicitFunction>;
using EvalDispatcher = vtkm::worklet::DispatcherMapField<EvalWorklet>;
auto s1 = vtkm::cont::make_ImplicitFunctionHandle(Internal.Sphere1);
auto s2 = vtkm::cont::make_ImplicitFunctionHandle(Internal.Sphere2);
EvalWorklet eval(s1.PrepareForExecution(DeviceAdapter()),
s2.PrepareForExecution(DeviceAdapter()));
Timer timer{ DeviceAdapter() };
timer.Start();
EvalDispatcher dispatcher(eval);
dispatcher.Invoke(this->Internal.Points, this->Internal.Result);
return timer.GetElapsedTime();
}
VTKM_CONT
std::string Description() const
{
std::stringstream description;
description << "Implicit Function 2x(VirtualImplicitFunction) on "
<< Internal.Points.GetNumberOfValues() << " points";
return description.str();
}
ImplicitFunctionBenchData Internal;
};
VTKM_MAKE_BENCHMARK(ImplicitFunction, BenchImplicitFunction);
VTKM_MAKE_BENCHMARK(ImplicitFunctionVirtual, BenchVirtualImplicitFunction);
VTKM_MAKE_BENCHMARK(ImplicitFunction2, Bench2ImplicitFunctions);
VTKM_MAKE_BENCHMARK(ImplicitFunctionVirtual2, Bench2VirtualImplicitFunctions);
public:
static VTKM_CONT int Run(int benchmarks, vtkm::cont::DeviceAdapterId id)
{
std::cout << DIVIDER << "\nRunning Field Algorithm benchmarks\n";
if (benchmarks & BLACK_SCHOLES)
{
std::cout << DIVIDER << "\nBenchmarking BlackScholes\n";
VTKM_RUN_BENCHMARK(BlackScholes, ValueTypes(), id);
VTKM_RUN_BENCHMARK(BlackScholesDynamic, ValueTypes(), id);
VTKM_RUN_BENCHMARK(BlackScholesMultiplexer0, ValueTypes(), id);
VTKM_RUN_BENCHMARK(BlackScholesMultiplexerN, ValueTypes(), id);
}
if (benchmarks & MATH)
{
std::cout << DIVIDER << "\nBenchmarking Multiple Math Worklets\n";
VTKM_RUN_BENCHMARK(Math, ValueTypes(), id);
VTKM_RUN_BENCHMARK(MathDynamic, ValueTypes(), id);
VTKM_RUN_BENCHMARK(MathMultiplexer0, ValueTypes(), id);
VTKM_RUN_BENCHMARK(MathMultiplexerN, ValueTypes(), id);
}
if (benchmarks & FUSED_MATH)
{
std::cout << DIVIDER << "\nBenchmarking Single Fused Math Worklet\n";
VTKM_RUN_BENCHMARK(FusedMath, ValueTypes(), id);
VTKM_RUN_BENCHMARK(FusedMathDynamic, ValueTypes(), id);
VTKM_RUN_BENCHMARK(FusedMathMultiplexer0, ValueTypes(), id);
VTKM_RUN_BENCHMARK(FusedMathMultiplexerN, ValueTypes(), id);
}
if (benchmarks & INTERPOLATE_FIELD)
{
std::cout << DIVIDER << "\nBenchmarking Edge Based Field InterpolationWorklet\n";
VTKM_RUN_BENCHMARK(EdgeInterp, InterpValueTypes(), id);
VTKM_RUN_BENCHMARK(EdgeInterpDynamic, InterpValueTypes(), id);
}
if (benchmarks & IMPLICIT_FUNCTION)
{
using FloatDefaultType = vtkm::ListTagBase<vtkm::FloatDefault>;
std::cout << "\nBenchmarking Implicit Function\n";
VTKM_RUN_BENCHMARK(ImplicitFunction, FloatDefaultType(), id);
VTKM_RUN_BENCHMARK(ImplicitFunctionVirtual, FloatDefaultType(), id);
VTKM_RUN_BENCHMARK(ImplicitFunction2, FloatDefaultType(), id);
VTKM_RUN_BENCHMARK(ImplicitFunctionVirtual2, FloatDefaultType(), id);
}
return 0;
}
};
#undef ARRAY_SIZE
}
} // namespace vtkm::benchmarking
int main(int argc, char* argv[])
{
auto opts = vtkm::cont::InitializeOptions::DefaultAnyDevice;
auto config = vtkm::cont::Initialize(argc, argv, opts);
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<char>(std::tolower(static_cast<unsigned char>(c)));
});
if (arg == "blackscholes")
{
benchmarks |= vtkm::benchmarking::BLACK_SCHOLES;
}
else if (arg == "math")
{
benchmarks |= vtkm::benchmarking::MATH;
}
else if (arg == "fusedmath")
{
benchmarks |= vtkm::benchmarking::FUSED_MATH;
}
else if (arg == "interpolate")
{
benchmarks |= vtkm::benchmarking::INTERPOLATE_FIELD;
}
else if (arg == "implicit_function")
{
benchmarks |= vtkm::benchmarking::IMPLICIT_FUNCTION;
}
else
{
std::cerr << "Unrecognized benchmark: " << argv[i] << std::endl;
std::cerr << "Usage: " << argv[0] << " [options] [benchmarks]" << std::endl;
std::cerr << "options are:" << std::endl;
std::cerr << config.Usage;
std::cerr << "available benchmarks are:" << std::endl;
std::cerr << " blackscholes, math, fusedmath, interpolate, implicit_function" << std::endl;
std::cerr << "If no benchmarks are specified, all are run." << std::endl;
return 1;
}
}
}
//now actually execute the benchmarks
return vtkm::benchmarking::BenchmarkFieldAlgorithms::Run(benchmarks, config.Device);
}