vtk-m/benchmarking/BenchmarkArrayTransfer.cxx
Haocheng LIU 634f523d92 Merge benchmark executables into a device dependent shared library
VTK-m has been updated to replace old per device benchmark executables with a device
dependent shared library so that it's able to accept a device adapter at runtime through
the "--device=" argument.
2019-02-25 12:26:47 -05:00

580 lines
18 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.
//
// Copyright 2017 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Copyright 2017 UT-Battelle, LLC.
// Copyright 2017 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 "Benchmarker.h"
#include <vtkm/TypeTraits.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/exec/FunctorBase.h>
#include <sstream>
#include <string>
#include <vector>
// 256 MB of floats:
const vtkm::Id ARRAY_SIZE = 256 * 1024 * 1024 / 4;
namespace vtkm
{
namespace benchmarking
{
struct BenchmarkArrayTransfer
{
using Algo = vtkm::cont::Algorithm;
using StorageTag = vtkm::cont::StorageTagBasic;
using Timer = vtkm::cont::Timer;
//------------- Functors for benchmarks --------------------------------------
// Reads all values in ArrayHandle.
template <typename PortalType>
struct ReadValues : vtkm::exec::FunctorBase
{
using ValueType = typename PortalType::ValueType;
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
PortalType Portal;
const ValueType MinValue;
VTKM_CONT
ReadValues(const PortalType& portal, const ValueType& minValue)
: Portal(portal)
, MinValue(minValue)
{
}
VTKM_EXEC
void operator()(vtkm::Id i) const
{
if (this->Portal.Get(i) < this->MinValue)
{
// We don't really do anything with this, we just need to do *something*
// to prevent the compiler from optimizing out the array accesses.
this->RaiseError("Unexpected value.");
}
}
// unused int argument is simply to distinguish this method from the
// VTKM_EXEC overload (VTKM_EXEC_CONT won't work here because of the
// RaiseError call).
VTKM_CONT
void operator()(vtkm::Id i, int) const
{
if (this->Portal.Get(i) < this->MinValue)
{
// We don't really do anything with this, we just need to do *something*
// to prevent the compiler from optimizing out the array accesses.
std::cerr << "Unexpected value.\n";
}
}
};
// Writes values to ArrayHandle.
template <typename PortalType>
struct WriteValues : vtkm::exec::FunctorBase
{
using ValueType = typename PortalType::ValueType;
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
PortalType Portal;
VTKM_CONT
WriteValues(const PortalType& portal)
: Portal(portal)
{
}
VTKM_EXEC_CONT
void operator()(vtkm::Id i) const { this->Portal.Set(i, static_cast<ValueType>(i)); }
};
// Reads and writes values to ArrayHandle.
template <typename PortalType>
struct ReadWriteValues : vtkm::exec::FunctorBase
{
using ValueType = typename PortalType::ValueType;
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
PortalType Portal;
VTKM_CONT
ReadWriteValues(const PortalType& portal)
: Portal(portal)
{
}
VTKM_EXEC_CONT
void operator()(vtkm::Id i) const
{
ValueType val = this->Portal.Get(i);
val += static_cast<ValueType>(i);
this->Portal.Set(i, val);
}
};
//------------- Benchmark functors -------------------------------------------
// Copies NumValues from control environment to execution environment and
// accesses them as read-only.
template <typename ValueType, typename DeviceAdapter>
struct BenchContToExecRead
{
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
using ValueTypeTraits = vtkm::TypeTraits<ValueType>;
vtkm::Id NumValues;
VTKM_CONT
BenchContToExecRead(vtkm::Id numValues)
: NumValues(numValues)
{
}
VTKM_CONT
std::string Description() const
{
std::ostringstream out;
out << "Copying from Control --> Execution (read-only): " << this->NumValues << " values ("
<< (this->NumValues * static_cast<vtkm::Id>(sizeof(ValueType))) << " bytes)";
return out.str();
}
VTKM_CONT
vtkm::Float64 operator()() const
{
std::vector<ValueType> vec(static_cast<std::size_t>(this->NumValues),
ValueTypeTraits::ZeroInitialization());
ArrayType array = vtkm::cont::make_ArrayHandle(vec);
// Time the copy:
Timer timer{ DeviceAdapter() };
timer.Start();
auto portal = array.PrepareForInput(DeviceAdapter());
ReadValues<decltype(portal)> functor(portal, ValueTypeTraits::ZeroInitialization());
Algo::Schedule(functor, this->NumValues);
return timer.GetElapsedTime();
}
};
VTKM_MAKE_BENCHMARK(ContToExecRead, BenchContToExecRead, ARRAY_SIZE);
// Writes values to ArrayHandle in execution environment. There is no actual
// copy between control/execution in this case.
template <typename ValueType, typename DeviceAdapter>
struct BenchContToExecWrite
{
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
using ValueTypeTraits = vtkm::TypeTraits<ValueType>;
vtkm::Id NumValues;
VTKM_CONT
BenchContToExecWrite(vtkm::Id numValues)
: NumValues(numValues)
{
}
VTKM_CONT
std::string Description() const
{
std::ostringstream out;
out << "Copying from Control --> Execution (write-only): " << this->NumValues << " values ("
<< (this->NumValues * static_cast<vtkm::Id>(sizeof(ValueType))) << " bytes)";
return out.str();
}
VTKM_CONT
vtkm::Float64 operator()() const
{
ArrayType array;
// Time the write:
Timer timer{ DeviceAdapter() };
timer.Start();
auto portal = array.PrepareForOutput(this->NumValues, DeviceAdapter());
WriteValues<decltype(portal)> functor(portal);
Algo::Schedule(functor, this->NumValues);
return timer.GetElapsedTime();
}
};
VTKM_MAKE_BENCHMARK(ContToExecWrite, BenchContToExecWrite, ARRAY_SIZE);
// Copies NumValues from control environment to execution environment and
// both reads and writes them.
template <typename ValueType, typename DeviceAdapter>
struct BenchContToExecReadWrite
{
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
using ValueTypeTraits = vtkm::TypeTraits<ValueType>;
vtkm::Id NumValues;
VTKM_CONT
BenchContToExecReadWrite(vtkm::Id numValues)
: NumValues(numValues)
{
}
VTKM_CONT
std::string Description() const
{
std::ostringstream out;
out << "Copying from Control --> Execution (read-write): " << this->NumValues << " values ("
<< (this->NumValues * static_cast<vtkm::Id>(sizeof(ValueType))) << " bytes)";
return out.str();
}
VTKM_CONT
vtkm::Float64 operator()() const
{
std::vector<ValueType> vec(static_cast<std::size_t>(this->NumValues),
ValueTypeTraits::ZeroInitialization());
ArrayType array = vtkm::cont::make_ArrayHandle(vec);
// Time the copy:
Timer timer{ DeviceAdapter() };
timer.Start();
auto portal = array.PrepareForInPlace(DeviceAdapter());
ReadWriteValues<decltype(portal)> functor(portal);
Algo::Schedule(functor, this->NumValues);
return timer.GetElapsedTime();
}
};
VTKM_MAKE_BENCHMARK(ContToExecReadWrite, BenchContToExecReadWrite, ARRAY_SIZE);
// Copies NumValues from control environment to execution environment and
// back, then accesses them as read-only.
template <typename ValueType, typename DeviceAdapter>
struct BenchRoundTripRead
{
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
using PortalContType = typename ArrayType::PortalConstControl;
using PortalExecType = typename ArrayType::template ExecutionTypes<DeviceAdapter>::PortalConst;
using ValueTypeTraits = vtkm::TypeTraits<ValueType>;
vtkm::Id NumValues;
VTKM_CONT
BenchRoundTripRead(vtkm::Id numValues)
: NumValues(numValues)
{
}
VTKM_CONT
std::string Description() const
{
std::ostringstream out;
out << "Copying from Control --> Execution --> Control (read-only): " << this->NumValues
<< " values (" << (this->NumValues * static_cast<vtkm::Id>(sizeof(ValueType)))
<< " bytes)";
return out.str();
}
VTKM_CONT
vtkm::Float64 operator()() const
{
std::vector<ValueType> vec(static_cast<std::size_t>(this->NumValues),
ValueTypeTraits::ZeroInitialization());
ArrayType array = vtkm::cont::make_ArrayHandle(vec);
// Ensure data is in control before we start:
array.ReleaseResourcesExecution();
// Time the copy:
Timer timer{ DeviceAdapter() };
timer.Start();
// Copy to device:
auto portal = array.PrepareForInput(DeviceAdapter());
ReadValues<PortalExecType> functor(portal, ValueTypeTraits::ZeroInitialization());
Algo::Schedule(functor, this->NumValues);
// Copy back to host and read:
ReadValues<PortalContType> cFunctor(array.GetPortalConstControl(),
ValueTypeTraits::ZeroInitialization());
for (vtkm::Id i = 0; i < this->NumValues; ++i)
{
cFunctor(i, 0);
}
return timer.GetElapsedTime();
}
};
VTKM_MAKE_BENCHMARK(RoundTripRead, BenchRoundTripRead, ARRAY_SIZE);
// Copies NumValues from control environment to execution environment and
// back, then reads and writes them in-place.
template <typename ValueType, typename DeviceAdapter>
struct BenchRoundTripReadWrite
{
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
using PortalContType = typename ArrayType::PortalControl;
using PortalExecType = typename ArrayType::template ExecutionTypes<DeviceAdapter>::Portal;
using ValueTypeTraits = vtkm::TypeTraits<ValueType>;
vtkm::Id NumValues;
VTKM_CONT
BenchRoundTripReadWrite(vtkm::Id numValues)
: NumValues(numValues)
{
}
VTKM_CONT
std::string Description() const
{
std::ostringstream out;
out << "Copying from Control --> Execution --> Control (read-write): " << this->NumValues
<< " values (" << (this->NumValues * static_cast<vtkm::Id>(sizeof(ValueType)))
<< " bytes)";
return out.str();
}
VTKM_CONT
vtkm::Float64 operator()() const
{
std::vector<ValueType> vec(static_cast<std::size_t>(this->NumValues),
ValueTypeTraits::ZeroInitialization());
ArrayType array = vtkm::cont::make_ArrayHandle(vec);
// Ensure data is in control before we start:
array.ReleaseResourcesExecution();
// Time the copy:
Timer timer{ DeviceAdapter() };
timer.Start();
// Do work on device:
auto portal = array.PrepareForInPlace(DeviceAdapter());
ReadWriteValues<PortalExecType> functor(portal);
Algo::Schedule(functor, this->NumValues);
ReadWriteValues<PortalContType> cFunctor(array.GetPortalControl());
for (vtkm::Id i = 0; i < this->NumValues; ++i)
{
cFunctor(i);
}
return timer.GetElapsedTime();
}
};
VTKM_MAKE_BENCHMARK(RoundTripReadWrite, BenchRoundTripReadWrite, ARRAY_SIZE);
// Write NumValues to device allocated memory and copies them back to control
// for reading.
template <typename ValueType, typename DeviceAdapter>
struct BenchExecToContRead
{
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
using PortalContType = typename ArrayType::PortalConstControl;
using PortalExecType = typename ArrayType::template ExecutionTypes<DeviceAdapter>::Portal;
using ValueTypeTraits = vtkm::TypeTraits<ValueType>;
vtkm::Id NumValues;
VTKM_CONT
BenchExecToContRead(vtkm::Id numValues)
: NumValues(numValues)
{
}
VTKM_CONT
std::string Description() const
{
std::ostringstream out;
out << "Copying from Execution --> Control (read-only on control): " << this->NumValues
<< " values (" << (this->NumValues * static_cast<vtkm::Id>(sizeof(ValueType)))
<< " bytes)";
return out.str();
}
VTKM_CONT
vtkm::Float64 operator()() const
{
ArrayType array;
// Time the copy:
Timer timer{ DeviceAdapter() };
timer.Start();
// Allocate/write data on device
auto portal = array.PrepareForOutput(this->NumValues, DeviceAdapter());
WriteValues<PortalExecType> functor(portal);
Algo::Schedule(functor, this->NumValues);
// Read back on host:
ReadValues<PortalContType> cFunctor(array.GetPortalConstControl(),
ValueTypeTraits::ZeroInitialization());
for (vtkm::Id i = 0; i < this->NumValues; ++i)
{
cFunctor(i, 0);
}
return timer.GetElapsedTime();
}
};
VTKM_MAKE_BENCHMARK(ExecToContRead, BenchExecToContRead, ARRAY_SIZE);
// Write NumValues to device allocated memory and copies them back to control
// and overwrites them.
template <typename ValueType, typename DeviceAdapter>
struct BenchExecToContWrite
{
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
using PortalContType = typename ArrayType::PortalControl;
using PortalExecType = typename ArrayType::template ExecutionTypes<DeviceAdapter>::Portal;
using ValueTypeTraits = vtkm::TypeTraits<ValueType>;
vtkm::Id NumValues;
VTKM_CONT
BenchExecToContWrite(vtkm::Id numValues)
: NumValues(numValues)
{
}
VTKM_CONT
std::string Description() const
{
std::ostringstream out;
out << "Copying from Execution --> Control (write-only on control): " << this->NumValues
<< " values (" << (this->NumValues * static_cast<vtkm::Id>(sizeof(ValueType)))
<< " bytes)";
return out.str();
}
VTKM_CONT
vtkm::Float64 operator()()
{
ArrayType array;
// Time the copy:
Timer timer{ DeviceAdapter() };
timer.Start();
// Allocate/write data on device
auto portal = array.PrepareForOutput(this->NumValues, DeviceAdapter());
WriteValues<PortalExecType> functor(portal);
Algo::Schedule(functor, this->NumValues);
// Read back on host:
WriteValues<PortalContType> cFunctor(array.GetPortalControl());
for (vtkm::Id i = 0; i < this->NumValues; ++i)
{
cFunctor(i);
}
return timer.GetElapsedTime();
}
};
VTKM_MAKE_BENCHMARK(ExecToContWrite, BenchExecToContWrite, ARRAY_SIZE);
// Write NumValues to device allocated memory and copies them back to control
// for reading and writing.
template <typename ValueType, typename DeviceAdapter>
struct BenchExecToContReadWrite
{
using ArrayType = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
using PortalContType = typename ArrayType::PortalControl;
using PortalExecType = typename ArrayType::template ExecutionTypes<DeviceAdapter>::Portal;
using ValueTypeTraits = vtkm::TypeTraits<ValueType>;
vtkm::Id NumValues;
VTKM_CONT
BenchExecToContReadWrite(vtkm::Id numValues)
: NumValues(numValues)
{
}
VTKM_CONT
std::string Description() const
{
std::ostringstream out;
out << "Copying from Execution --> Control (read-write on control): " << this->NumValues
<< " values (" << (this->NumValues * static_cast<vtkm::Id>(sizeof(ValueType)))
<< " bytes)";
return out.str();
}
VTKM_CONT
vtkm::Float64 operator()()
{
ArrayType array;
// Time the copy:
Timer timer{ DeviceAdapter() };
timer.Start();
// Allocate/write data on device
auto portal = array.PrepareForOutput(this->NumValues, DeviceAdapter());
WriteValues<PortalExecType> functor(portal);
Algo::Schedule(functor, this->NumValues);
// Read back on host:
ReadWriteValues<PortalContType> cFunctor(array.GetPortalControl());
for (vtkm::Id i = 0; i < this->NumValues; ++i)
{
cFunctor(i);
}
return timer.GetElapsedTime();
}
};
VTKM_MAKE_BENCHMARK(ExecToContReadWrite, BenchExecToContReadWrite, ARRAY_SIZE);
//----------- Benchmark caller -----------------------------------------------
using TestTypes = vtkm::ListTagBase<vtkm::Float32>;
static VTKM_CONT bool Run(vtkm::cont::DeviceAdapterId id)
{
VTKM_RUN_BENCHMARK(ContToExecRead, TestTypes(), id);
VTKM_RUN_BENCHMARK(ContToExecWrite, TestTypes(), id);
VTKM_RUN_BENCHMARK(ContToExecReadWrite, TestTypes(), id);
VTKM_RUN_BENCHMARK(RoundTripRead, TestTypes(), id);
VTKM_RUN_BENCHMARK(RoundTripReadWrite, TestTypes(), id);
VTKM_RUN_BENCHMARK(ExecToContRead, TestTypes(), id);
VTKM_RUN_BENCHMARK(ExecToContWrite, TestTypes(), id);
VTKM_RUN_BENCHMARK(ExecToContReadWrite, TestTypes(), id);
return true;
}
};
}
} // end namespace vtkm::benchmarking
int main(int argc, char* argv[])
{
auto opts = vtkm::cont::InitializeOptions::RequireDevice;
auto config = vtkm::cont::Initialize(argc, argv, opts);
using Benchmarks = vtkm::benchmarking::BenchmarkArrayTransfer;
bool result = Benchmarks::Run(config.Device);
return result ? EXIT_SUCCESS : EXIT_FAILURE;
}