Add const correctness to Timer

It should be possible to query a vtkm::cont::Timer without modifying it.
As such, its query functions (such as Stopped and GetElapsedTime) should
be const.
This commit is contained in:
Kenneth Moreland 2019-02-28 11:32:09 -07:00
parent 4655089934
commit 85265a9c84
6 changed files with 247 additions and 192 deletions

@ -573,11 +573,11 @@ public:
this->StopReady = true;
}
VTKM_CONT bool Started() { return this->StartReady; }
VTKM_CONT bool Started() const { return this->StartReady; }
VTKM_CONT bool Stopped() { return this->StopReady; }
VTKM_CONT bool Stopped() const { return this->StopReady; }
VTKM_CONT bool Ready() { return true; }
VTKM_CONT bool Ready() const { return true; }
/// Returns the elapsed time in seconds between the construction of this
/// class or the last call to Reset and the time this function is called. The
@ -585,7 +585,7 @@ public:
/// number of times to get the progressive time. This method synchronizes all
/// asynchronous operations.
///
VTKM_CONT vtkm::Float64 GetElapsedTime()
VTKM_CONT vtkm::Float64 GetElapsedTime() const
{
assert(this->StartReady);
if (!this->StartReady)
@ -595,23 +595,18 @@ public:
return 0;
}
bool manualStop = true;
if (!this->StopReady)
{
manualStop = false;
this->Stop();
}
TimeStamp startTime = this->StartTime;
TimeStamp stopTime = this->StopReady ? this->StopTime : this->GetCurrentTime();
vtkm::Float64 elapsedTime;
elapsedTime = vtkm::Float64(this->StopTime.Seconds - this->StartTime.Seconds);
elapsedTime += (vtkm::Float64(this->StopTime.Microseconds - this->StartTime.Microseconds) /
vtkm::Float64(1000000));
// Reset StopReady flag to its original state
this->StopReady = manualStop;
elapsedTime = vtkm::Float64(stopTime.Seconds - startTime.Seconds);
elapsedTime +=
(vtkm::Float64(stopTime.Microseconds - startTime.Microseconds) / vtkm::Float64(1000000));
return elapsedTime;
}
VTKM_CONT TimeStamp GetCurrentTime()
VTKM_CONT TimeStamp GetCurrentTime() const
{
vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapterTag>::Synchronize();

@ -23,12 +23,7 @@
#include <vtkm/internal/brigand.hpp>
namespace vtkm
{
namespace cont
{
namespace detail
namespace
{
template <typename State, typename T>
struct RemoveDisabledDevice
@ -37,31 +32,26 @@ struct RemoveDisabledDevice
};
/// TMP code to generate enabled device timer container
using AllDeviceList = DeviceAdapterListTagCommon::list;
using EnabledDeviceList =
brigand::fold<AllDeviceList,
brigand::list<>,
detail::RemoveDisabledDevice<brigand::_state, brigand::_element>>;
using AllDeviceList = vtkm::cont::DeviceAdapterListTagCommon::list;
using EnabledDeviceList = brigand::fold<AllDeviceList,
brigand::list<>,
RemoveDisabledDevice<brigand::_state, brigand::_element>>;
struct EnabledDeviceListTag : vtkm::ListTagBase<>
{
using list = EnabledDeviceList;
};
using EnabledTimerImpls =
brigand::transform<EnabledDeviceList,
brigand::bind<DeviceAdapterTimerImplementation, brigand::_1>>;
brigand::bind<vtkm::cont::DeviceAdapterTimerImplementation, brigand::_1>>;
using EnabledTimerImplTuple = brigand::as_tuple<EnabledTimerImpls>;
}
} // anonymous namespace
enum class TimerDispatchTag : int
namespace vtkm
{
namespace cont
{
namespace detail
{
Reset,
Start,
Stop,
Started,
Stopped,
Ready,
GetElapsedTime
};
class EnabledDeviceTimerImpls
{
@ -69,11 +59,15 @@ public:
EnabledDeviceTimerImpls() {}
~EnabledDeviceTimerImpls() {}
// A tuple of enabled timer implementations
detail::EnabledTimerImplTuple timerImplTuple;
EnabledTimerImplTuple timerImplTuple;
};
}
}
} // namespace vtkm::cont::detail
namespace detail
namespace
{
// C++11 does not support get tuple element by type. C++14 does support that.
// Get the index of a type in tuple elements
template <class T, class Tuple>
@ -91,75 +85,136 @@ struct Index<T, Container<U, Types...>>
static const std::size_t value = 1 + Index<T, Container<Types...>>::value;
};
struct TimerFunctor
template <typename Device>
VTKM_CONT inline auto GetTimerImpl(Device, vtkm::cont::detail::EnabledDeviceTimerImpls* timerImpls)
-> decltype(std::get<Index<Device, EnabledDeviceList>::value>(timerImpls->timerImplTuple))
{
TimerFunctor()
: elapsedTime(0)
, value(true)
{
}
template <typename DeviceAdapter>
VTKM_CONT void operator()(DeviceAdapter device, Timer* timer, TimerDispatchTag tag)
{
if (timer->Device == device || timer->Device == DeviceAdapterTagAny())
{
auto& timerImpl = std::get<Index<DeviceAdapter, detail::EnabledDeviceList>::value>(
timer->Internal->timerImplTuple);
switch (tag)
{
case TimerDispatchTag::Reset:
timerImpl.Reset();
break;
case TimerDispatchTag::Start:
timerImpl.Start();
break;
case TimerDispatchTag::Stop:
timerImpl.Stop();
break;
case TimerDispatchTag::Started:
value &= timerImpl.Started();
break;
case TimerDispatchTag::Stopped:
value &= timerImpl.Stopped();
break;
case TimerDispatchTag::Ready:
value &= timerImpl.Ready();
break;
case TimerDispatchTag::GetElapsedTime:
{
if (timer->Device == DeviceAdapterTagAny() &&
timer->DeviceForQuery == DeviceAdapterTagAny())
{ // Just want to do timing jobs
elapsedTime = std::max(elapsedTime, timerImpl.GetElapsedTime());
break;
}
else if (timer->Device == DeviceAdapterTagAny() && timer->DeviceForQuery == device)
{ // Given a generic timer, querying for a specific device time
elapsedTime = timerImpl.GetElapsedTime();
break;
}
else if (timer->Device == device && (timer->DeviceForQuery == DeviceAdapterTagAny() ||
timer->Device == timer->DeviceForQuery))
{ // Given a specific timer, querying its elapsed time
elapsedTime = timerImpl.GetElapsedTime();
break;
}
break;
}
}
}
}
vtkm::Float64 elapsedTime;
bool value;
};
return std::get<Index<Device, EnabledDeviceList>::value>(timerImpls->timerImplTuple);
}
template <typename Device>
VTKM_CONT inline auto GetTimerImpl(Device,
const vtkm::cont::detail::EnabledDeviceTimerImpls* timerImpls)
-> decltype(std::get<Index<Device, EnabledDeviceList>::value>(timerImpls->timerImplTuple))
{
return std::get<Index<Device, EnabledDeviceList>::value>(timerImpls->timerImplTuple);
}
struct ResetFunctor
{
template <typename Device>
VTKM_CONT void operator()(Device device,
vtkm::cont::Timer* timer,
vtkm::cont::detail::EnabledDeviceTimerImpls* timerImpls)
{
if ((timer->GetDevice() == device) || (timer->GetDevice() == vtkm::cont::DeviceAdapterTagAny()))
{
GetTimerImpl(device, timerImpls).Reset();
}
}
};
struct StartFunctor
{
template <typename Device>
VTKM_CONT void operator()(Device device,
vtkm::cont::Timer* timer,
vtkm::cont::detail::EnabledDeviceTimerImpls* timerImpls)
{
if ((timer->GetDevice() == device) || (timer->GetDevice() == vtkm::cont::DeviceAdapterTagAny()))
{
GetTimerImpl(device, timerImpls).Start();
}
}
};
struct StopFunctor
{
template <typename Device>
VTKM_CONT void operator()(Device device,
vtkm::cont::Timer* timer,
vtkm::cont::detail::EnabledDeviceTimerImpls* timerImpls)
{
if ((timer->GetDevice() == device) || (timer->GetDevice() == vtkm::cont::DeviceAdapterTagAny()))
{
GetTimerImpl(device, timerImpls).Stop();
}
}
};
struct StartedFunctor
{
bool Value = true;
template <typename Device>
VTKM_CONT void operator()(Device device,
const vtkm::cont::Timer* timer,
const vtkm::cont::detail::EnabledDeviceTimerImpls* timerImpls)
{
if ((timer->GetDevice() == device) || (timer->GetDevice() == vtkm::cont::DeviceAdapterTagAny()))
{
this->Value &= GetTimerImpl(device, timerImpls).Started();
}
}
};
struct StoppedFunctor
{
bool Value = true;
template <typename Device>
VTKM_CONT void operator()(Device device,
const vtkm::cont::Timer* timer,
const vtkm::cont::detail::EnabledDeviceTimerImpls* timerImpls)
{
if ((timer->GetDevice() == device) || (timer->GetDevice() == vtkm::cont::DeviceAdapterTagAny()))
{
this->Value &= GetTimerImpl(device, timerImpls).Stopped();
}
}
};
struct ReadyFunctor
{
bool Value = true;
template <typename Device>
VTKM_CONT void operator()(Device device,
const vtkm::cont::Timer* timer,
const vtkm::cont::detail::EnabledDeviceTimerImpls* timerImpls)
{
if ((timer->GetDevice() == device) || (timer->GetDevice() == vtkm::cont::DeviceAdapterTagAny()))
{
this->Value &= GetTimerImpl(device, timerImpls).Ready();
}
}
};
struct ElapsedTimeFunctor
{
vtkm::Float64 ElapsedTime = 0.0;
template <typename Device>
VTKM_CONT void operator()(Device deviceToTry,
vtkm::cont::DeviceAdapterId deviceToRunOn,
const vtkm::cont::detail::EnabledDeviceTimerImpls* timerImpls)
{
if ((deviceToRunOn == deviceToTry) || (deviceToRunOn == vtkm::cont::DeviceAdapterTagAny()))
{
this->ElapsedTime =
vtkm::Max(this->ElapsedTime, GetTimerImpl(deviceToTry, timerImpls).GetElapsedTime());
}
}
};
} // anonymous namespace
namespace vtkm
{
namespace cont
{
Timer::Timer()
: Device(vtkm::cont::DeviceAdapterTagAny())
, DeviceForQuery(vtkm::cont::DeviceAdapterTagAny())
, Internal(nullptr)
{
this->Init();
@ -167,10 +222,9 @@ Timer::Timer()
Timer::Timer(vtkm::cont::DeviceAdapterId device)
: Device(device)
, DeviceForQuery(vtkm::cont::DeviceAdapterTagAny())
, Internal(nullptr)
{
vtkm::cont::RuntimeDeviceTracker tracker;
vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker();
if (device != DeviceAdapterTagAny() && !tracker.CanRunOn(device))
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
@ -182,28 +236,24 @@ Timer::Timer(vtkm::cont::DeviceAdapterId device)
}
Timer::~Timer()
{
delete this->Internal;
}
Timer::~Timer() = default;
void Timer::Init()
{
if (!this->Internal)
{
this->Internal = new EnabledDeviceTimerImpls();
this->Internal.reset(new detail::EnabledDeviceTimerImpls);
}
}
void Timer::Reset()
{
detail::TimerFunctor functor;
vtkm::ListForEach(functor, detail::EnabledDeviceListTag(), this, TimerDispatchTag::Reset);
vtkm::ListForEach(ResetFunctor(), EnabledDeviceListTag(), this, this->Internal.get());
}
void Timer::Reset(vtkm::cont::DeviceAdapterId device)
{
vtkm::cont::RuntimeDeviceTracker tracker;
vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker();
if (device != DeviceAdapterTagAny() && !tracker.CanRunOn(device))
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
@ -212,74 +262,83 @@ void Timer::Reset(vtkm::cont::DeviceAdapterId device)
}
this->Device = device;
this->DeviceForQuery = vtkm::cont::DeviceAdapterTagAny();
this->Reset();
}
void Timer::Start()
{
detail::TimerFunctor functor;
vtkm::ListForEach(functor, detail::EnabledDeviceListTag(), this, TimerDispatchTag::Start);
vtkm::ListForEach(StartFunctor(), EnabledDeviceListTag(), this, this->Internal.get());
}
void Timer::Stop()
{
detail::TimerFunctor functor;
vtkm::ListForEach(functor, detail::EnabledDeviceListTag(), this, TimerDispatchTag::Stop);
vtkm::ListForEach(StopFunctor(), EnabledDeviceListTag(), this, this->Internal.get());
}
bool Timer::Started()
bool Timer::Started() const
{
detail::TimerFunctor functor;
vtkm::ListForEach(functor, detail::EnabledDeviceListTag(), this, TimerDispatchTag::Started);
return functor.value;
StartedFunctor functor;
vtkm::ListForEach(functor, EnabledDeviceListTag(), this, this->Internal.get());
return functor.Value;
}
bool Timer::Stopped()
bool Timer::Stopped() const
{
detail::TimerFunctor functor;
vtkm::ListForEach(functor, detail::EnabledDeviceListTag(), this, TimerDispatchTag::Stopped);
return functor.value;
StoppedFunctor functor;
vtkm::ListForEach(functor, EnabledDeviceListTag(), this, this->Internal.get());
return functor.Value;
}
bool Timer::Ready()
bool Timer::Ready() const
{
detail::TimerFunctor functor;
vtkm::ListForEach(functor, detail::EnabledDeviceListTag(), this, TimerDispatchTag::Ready);
return functor.value;
ReadyFunctor functor;
vtkm::ListForEach(functor, EnabledDeviceListTag(), this, this->Internal.get());
return functor.Value;
}
vtkm::Float64 Timer::GetElapsedTime(DeviceAdapterId id)
vtkm::Float64 Timer::GetElapsedTime(vtkm::cont::DeviceAdapterId device) const
{
// Timer is constructed with a specific device. Only querying this device is allowed.
if (this->Device != DeviceAdapterTagAny() && (id != DeviceAdapterTagAny() && this->Device != id))
vtkm::cont::DeviceAdapterId deviceToTime = device;
if (this->Device != DeviceAdapterTagAny())
{
// Timer is constructed for a specific device. Only querying on this device is allowed.
if (deviceToTime == vtkm::cont::DeviceAdapterTagAny())
{
// User did not specify a device to time on. Use the one set in the timer.
deviceToTime = this->Device;
}
else if (deviceToTime == this->Device)
{
// User asked for the same device already set for the timer. We are OK. Nothing to do.
}
else
{
// The user selected a device that is differnt than the one set for the timer. This query
// is not allowed.
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Device '" << device.GetName() << "' is not supported for current timer"
<< "("
<< this->Device.GetName()
<< ")");
return 0.0;
}
}
// If we have specified a specific device, make sure we can run on it.
vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker();
if ((deviceToTime != vtkm::cont::DeviceAdapterTagAny()) && !tracker.CanRunOn(deviceToTime))
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Device '" << id.GetName() << "' is not supported for current timer"
<< "("
<< this->Device.GetName()
<< ")");
"Device '" << device.GetName() << "' can not run on current Device."
"Thus timer is not usable");
return 0.0;
}
// Timer is constructed with any device. Only querying enabled device is allowed.
vtkm::cont::RuntimeDeviceTracker tracker;
if (this->Device == DeviceAdapterTagAny() &&
(id != DeviceAdapterTagAny() && !tracker.CanRunOn(id)))
{
VTKM_LOG_S(vtkm::cont::LogLevel::Error,
"Device '" << id.GetName() << "' can not run on current Device."
"Thus timer is not usable");
return 0.0;
}
ElapsedTimeFunctor functor;
vtkm::ListForEach(functor, EnabledDeviceListTag(), deviceToTime, this->Internal.get());
this->DeviceForQuery = id;
detail::TimerFunctor functor;
vtkm::ListForEach(
functor, detail::EnabledDeviceListTag(), this, TimerDispatchTag::GetElapsedTime);
return functor.elapsedTime;
return functor.ElapsedTime;
}
}
} // namespace vtkm::cont

@ -26,15 +26,16 @@
#include <vtkm/cont/vtkm_cont_export.h>
#include <memory>
namespace vtkm
{
namespace cont
{
namespace detail
{
struct TimerFunctor;
}
class EnabledDeviceTimerImpls;
}
/// A class that can be used to time operations in VTK-m that might be occuring
/// in parallel. Users are recommended to provide a device adapter at construction
@ -53,8 +54,6 @@ class EnabledDeviceTimerImpls;
///
class VTKM_CONT_EXPORT Timer
{
friend struct detail::TimerFunctor;
public:
VTKM_CONT
Timer();
@ -74,17 +73,22 @@ public:
VTKM_CONT void Stop();
VTKM_CONT bool Started();
VTKM_CONT bool Started() const;
VTKM_CONT bool Stopped();
VTKM_CONT bool Stopped() const;
/// Used to check if Timer has finished the synchronization to get the result from the device.
VTKM_CONT bool Ready();
VTKM_CONT bool Ready() const;
/// Get the elapsed time measured by the given device adapter. If no device is
/// specified, the max time of all device measurements will be returned.
VTKM_CONT
vtkm::Float64 GetElapsedTime(DeviceAdapterId id = DeviceAdapterTagAny());
vtkm::Float64 GetElapsedTime(
vtkm::cont::DeviceAdapterId id = vtkm::cont::DeviceAdapterTagAny()) const;
/// Returns the device for which this timer is synchronized. If the device adapter has the same
/// id as DeviceAdapterTagAny, then the timer will synchronize all devices.
VTKM_CONT vtkm::cont::DeviceAdapterId GetDevice() const { return this->Device; }
private:
VTKM_CONT void Init();
@ -93,8 +97,7 @@ private:
VTKM_CONT void operator=(const Timer&) = delete;
DeviceAdapterId Device;
DeviceAdapterId DeviceForQuery;
EnabledDeviceTimerImpls* Internal;
std::unique_ptr<detail::EnabledDeviceTimerImpls> Internal;
};
}
} // namespace vtkm::cont

@ -67,12 +67,12 @@ void DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Stop()
this->StopReady = true;
}
bool DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Started()
bool DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Started() const
{
return this->StartReady;
}
bool DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Stopped()
bool DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Stopped() const
{
return this->StopReady;
}
@ -80,7 +80,7 @@ bool DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Stopped
// Callbacks without a mandated order(in independent streams) execute in undefined
// order and maybe serialized. So Instead CudaEventQuery is used here.
// Ref link: https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__STREAM.html
bool DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Ready()
bool DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Ready() const
{
if (cudaEventQuery(this->StopEvent) == cudaSuccess)
{
@ -91,6 +91,7 @@ bool DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Ready()
vtkm::Float64 DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::GetElapsedTime()
const
{
assert(this->StartReady);
if (!this->StartReady)
@ -99,18 +100,16 @@ vtkm::Float64 DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>
"Start() function should be called first then trying to call GetElapsedTime().");
return 0;
}
bool manualStop = true;
if (!this->StopReady)
{
manualStop = false;
this->Stop();
// Stop was not called, so we have to insert a new event into the stream
VTKM_CUDA_CALL(cudaEventRecord(this->StopEvent, cudaStreamPerThread));
}
VTKM_CUDA_CALL(cudaEventSynchronize(this->StopEvent));
float elapsedTimeMilliseconds;
VTKM_CUDA_CALL(cudaEventElapsedTime(&elapsedTimeMilliseconds, this->StartEvent, this->StopEvent));
// Reset Stop flag to its original state
this->StopReady = manualStop;
return static_cast<vtkm::Float64>(0.001f * elapsedTimeMilliseconds);
}
}

@ -54,13 +54,13 @@ public:
VTKM_CONT void Stop();
VTKM_CONT bool Started();
VTKM_CONT bool Started() const;
VTKM_CONT bool Stopped();
VTKM_CONT bool Stopped() const;
VTKM_CONT bool Ready();
VTKM_CONT bool Ready() const;
VTKM_CONT vtkm::Float64 GetElapsedTime();
VTKM_CONT vtkm::Float64 GetElapsedTime() const;
private:
// Copying CUDA events is problematic.

@ -339,7 +339,6 @@ public:
VTKM_CONT void Reset()
{
vtkm::cont::DeviceAdapterAlgorithm<vtkm::cont::DeviceAdapterTagTBB>::Synchronize();
this->StartReady = false;
this->StopReady = false;
}
@ -347,23 +346,23 @@ public:
VTKM_CONT void Start()
{
this->Reset();
this->StartTime = ::tbb::tick_count::now();
this->StartTime = this->GetCurrentTime();
this->StartReady = true;
}
VTKM_CONT void Stop()
{
this->StopTime = ::tbb::tick_count::now();
this->StopTime = this->GetCurrentTime();
this->StopReady = true;
}
VTKM_CONT bool Started() { return this->StartReady; }
VTKM_CONT bool Started() const { return this->StartReady; }
VTKM_CONT bool Stopped() { return this->StopReady; }
VTKM_CONT bool Stopped() const { return this->StopReady; }
VTKM_CONT bool Ready() { return true; }
VTKM_CONT bool Ready() const { return true; }
VTKM_CONT vtkm::Float64 GetElapsedTime()
VTKM_CONT vtkm::Float64 GetElapsedTime() const
{
assert(this->StartReady);
if (!this->StartReady)
@ -373,21 +372,21 @@ public:
" GetElapsedTime().");
return 0;
}
bool manualStop = true;
if (!this->StopReady)
{
manualStop = false;
this->Stop();
}
vtkm::cont::DeviceAdapterAlgorithm<vtkm::cont::DeviceAdapterTagTBB>::Synchronize();
::tbb::tick_count::interval_t elapsedTime = this->StopTime - this->StartTime;
// Reset StopReady flag to its original state
this->StopReady = manualStop;
::tbb::tick_count startTime = this->StartTime;
::tbb::tick_count stopTime = this->StopReady ? this->StopTime : this->GetCurrentTime();
::tbb::tick_count::interval_t elapsedTime = stopTime - startTime;
return static_cast<vtkm::Float64>(elapsedTime.seconds());
}
VTKM_CONT::tbb::tick_count GetCurrentTime() const
{
vtkm::cont::DeviceAdapterAlgorithm<vtkm::cont::DeviceAdapterTagTBB>::Synchronize();
return ::tbb::tick_count::now();
}
private:
bool StartReady;
bool StopReady;