Commit 03fc3d51 authored by Kenneth Moreland's avatar Kenneth Moreland Committed by Kitware Robot

Merge topic 'timer-init-runtime-device'

6797c6e3 Specify return type for GetTimerImpl
25f3432b Increase the conditions on which Timer is tested
4d9ce248 Synchronize CUDA timer when stopping it
85265a9c Add const correctness to Timer
46550899 Allow resetting Timer with a new device
dd4a9395 Enable initializing Timer with a DeviceAdapterId
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Acked-by: Haocheng LIU's avatarHaocheng LIU <haocheng.liu@kitware.com>
Merge-request: !1562
parents 126aeabe 6797c6e3
......@@ -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();
......
This diff is collapsed.
......@@ -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
......@@ -45,50 +46,49 @@ class EnabledDeviceTimerImpls;
/// have the longest execution time if enabled.
/// Per device adapter time query is also supported. It's useful when users want to reuse
/// the same timer to measure the cuda kernal call as well as the cuda device execution.
/// It is also possible to change the device adapter after construction by calling the form
/// of the Reset method with a new DeviceAdapterId.
///
/// The there is no guaranteed resolution of the time but should generally be
/// good to about a millisecond.
///
class VTKM_CONT_EXPORT Timer
{
friend struct detail::TimerFunctor;
public:
VTKM_CONT
Timer();
template <typename DeviceAdapter>
VTKM_CONT Timer(DeviceAdapter id)
: Device(id)
, DeviceForQuery(DeviceAdapterTagAny())
, Internal(nullptr)
{
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapter);
static_assert(DeviceAdapter::IsEnabled, "A disabled device is passed to the Timer");
this->Init();
}
VTKM_CONT Timer(vtkm::cont::DeviceAdapterId device);
VTKM_CONT ~Timer();
/// Resets the timer.
VTKM_CONT void Reset();
/// Resets the timer and changes the device to time on.
VTKM_CONT void Reset(vtkm::cont::DeviceAdapterId device);
/// Start would call Reset function before starting the timer for convenience
VTKM_CONT void Start();
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();
......@@ -97,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
......
......@@ -64,15 +64,16 @@ void DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Start()
void DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Stop()
{
VTKM_CUDA_CALL(cudaEventRecord(this->StopEvent, cudaStreamPerThread));
VTKM_CUDA_CALL(cudaEventSynchronize(this->StopEvent));
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 +81,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 +92,7 @@ bool DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::Ready()
vtkm::Float64 DeviceAdapterTimerImplementation<vtkm::cont::DeviceAdapterTagCuda>::GetElapsedTime()
const
{
assert(this->StartReady);
if (!this->StartReady)
......@@ -99,18 +101,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));
}
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.
......
......@@ -341,7 +341,6 @@ public:
VTKM_CONT void Reset()
{
vtkm::cont::DeviceAdapterAlgorithm<vtkm::cont::DeviceAdapterTagTBB>::Synchronize();
this->StartReady = false;
this->StopReady = false;
}
......@@ -349,23 +348,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)
......@@ -375,21 +374,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;
......
......@@ -18,117 +18,156 @@
// this software.
//============================================================================
#include <vtkm/cont/DeviceAdapterListTag.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/cont/cuda/internal/DeviceAdapterTagCuda.h>
#include <vtkm/cont/openmp/internal/DeviceAdapterTagOpenMP.h>
#include <vtkm/cont/serial/internal/DeviceAdapterTagSerial.h>
#include <vtkm/cont/tbb/internal/DeviceAdapterTagTBB.h>
#include <vtkm/cont/internal/DeviceAdapterError.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/internal/Windows.h>
namespace
{
void Time()
struct TimerTestDevices
: vtkm::ListTagAppend<VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG, vtkm::cont::DeviceAdapterTagAny>
{
vtkm::cont::Timer timer;
timer.Start();
VTKM_TEST_ASSERT(timer.Started(), "Timer fails to track started status");
VTKM_TEST_ASSERT(!timer.Stopped(), "Timer fails to track non stopped status");
};
void WaitASec()
{
std::cout << " Sleeping for 1 second" << std::endl;
#ifdef VTKM_WINDOWS
Sleep(1000);
#else
sleep(1);
#endif
}
vtkm::Float64 elapsedTime = timer.GetElapsedTime();
VTKM_TEST_ASSERT(!timer.Stopped(), "Timer fails to track stopped status");
std::cout << "Elapsed time measured by any Tag: " << elapsedTime << std::endl;
VTKM_TEST_ASSERT(elapsedTime > 0.999, "General Timer did not capture full second wait.");
VTKM_TEST_ASSERT(elapsedTime < 2.0, "General Timer counted too far or system really busy.");
vtkm::cont::RuntimeDeviceTracker tracker;
vtkm::Float64 elapsedTimeCuda = timer.GetElapsedTime(vtkm::cont::DeviceAdapterTagCuda());
if (tracker.CanRunOn(vtkm::cont::DeviceAdapterTagCuda()))
{
std::cout << " can on run cuda?: true" << std::endl;
std::cout << "Elapsed time measured by cuda Tag: " << elapsedTime << std::endl;
VTKM_TEST_ASSERT(elapsedTimeCuda > 0.999, "Cuda Timer did not capture full second wait.");
VTKM_TEST_ASSERT(elapsedTimeCuda < 2.0, "Cuda Timer counted too far or system really busy.");
}
else
bool CanTimeOnDevice(const vtkm::cont::Timer& timer, vtkm::cont::DeviceAdapterId device)
{
if (device == vtkm::cont::DeviceAdapterTagAny())
{
VTKM_TEST_ASSERT(elapsedTimeCuda == 0.0, "Disabled Cuda Timer should return nothing.");
// The timer can run on any device. It should pick up something (unless perhaps there are no
// devices, which would only happen if you explicitly disable serial, which we don't).
return true;
}
vtkm::Float64 elapsedTimeSerial = timer.GetElapsedTime(vtkm::cont::DeviceAdapterTagSerial());
std::cout << "Elapsed time measured by serial Tag: " << elapsedTime << std::endl;
VTKM_TEST_ASSERT(elapsedTimeSerial > 0.999, "Serial Timer did not capture full second wait.");
VTKM_TEST_ASSERT(elapsedTimeSerial < 2.0, "Serial Timer counted too far or system really busy.");
vtkm::Float64 elapsedTimeOpenMP = timer.GetElapsedTime(vtkm::cont::DeviceAdapterTagOpenMP());
if (vtkm::cont::DeviceAdapterTagOpenMP::IsEnabled)
else if ((timer.GetDevice() == vtkm::cont::DeviceAdapterTagAny()) ||
(timer.GetDevice() == device))
{
std::cout << "Elapsed time measured by openmp Tag: " << elapsedTime << std::endl;
VTKM_TEST_ASSERT(elapsedTimeOpenMP > 0.999, "OpenMP Timer did not capture full second wait.");
VTKM_TEST_ASSERT(elapsedTimeOpenMP < 2.0,
"OpenMP Timer counted too far or system really busy.");
// Device is specified and it is a match for the timer's device.
return vtkm::cont::GetGlobalRuntimeDeviceTracker().CanRunOn(device);
}
else
{
VTKM_TEST_ASSERT(elapsedTimeOpenMP == 0.0, "Disabled OpenMP Timer should return nothing.");
// The requested device does not match the device of the timer.
return false;
}
}
vtkm::Float64 elapsedTimeTBB = timer.GetElapsedTime(vtkm::cont::DeviceAdapterTagTBB());
if (vtkm::cont::DeviceAdapterTagTBB::IsEnabled)
{
std::cout << "Elapsed time measured by tbb Tag: " << elapsedTime << std::endl;
VTKM_TEST_ASSERT(elapsedTimeTBB > 0.999, "TBB Timer did not capture full second wait.");
VTKM_TEST_ASSERT(elapsedTimeTBB < 2.0, "TBB Timer counted too far or system really busy.");
}
else
struct CheckTimeForDeviceFunctor
{
void operator()(vtkm::cont::DeviceAdapterId device,
const vtkm::cont::Timer& timer,
vtkm::Float64 expectedTime) const
{
VTKM_TEST_ASSERT(elapsedTimeOpenMP == 0.0, "Disabled TBB Timer should return nothing.");
std::cout << " Checking time for device " << device.GetName() << std::endl;
if (CanTimeOnDevice(timer, device))
{
vtkm::Float64 elapsedTime = timer.GetElapsedTime(device);
VTKM_TEST_ASSERT(
elapsedTime > (expectedTime - 0.001), "Timer did not capture full wait. ", elapsedTime);
VTKM_TEST_ASSERT(elapsedTime < (expectedTime + 1.0),
"Timer counted too far or system really busy. ",
elapsedTime);
}
else
{
std::cout << " Device not supported. Expect 0 back and possible error in log."
<< std::endl;
VTKM_TEST_ASSERT(timer.GetElapsedTime(device) == 0.0,
"Disabled timer should return nothing.");
}
}
};
std::cout << "Reuse the timer to test continuous timing." << std::endl;
#ifdef VTKM_WINDOWS
Sleep(1000);
#else
sleep(1);
#endif
void CheckTime(const vtkm::cont::Timer& timer, vtkm::Float64 expectedTime)
{
vtkm::ListForEach(CheckTimeForDeviceFunctor(), TimerTestDevices(), timer, expectedTime);
}
void DoTimerCheck(vtkm::cont::Timer& timer)
{
std::cout << " Starting timer" << std::endl;
timer.Start();
VTKM_TEST_ASSERT(timer.Started(), "Timer fails to track started status");
VTKM_TEST_ASSERT(!timer.Stopped(), "Timer fails to track non stopped status");
elapsedTime = timer.GetElapsedTime();
WaitASec();
std::cout << "Elapsed time measured by any Tag: " << elapsedTime << std::endl;
VTKM_TEST_ASSERT(elapsedTime > 1.999,
"General Timer did not capture two full seconds wait in second launch.");
VTKM_TEST_ASSERT(elapsedTime < 3.0,
"General Timer counted too far or system really busy in second launch.");
std::cout << " Check time for 1sec" << std::endl;
CheckTime(timer, 1.0);
std::cout << " Make sure timer is still running" << std::endl;
VTKM_TEST_ASSERT(!timer.Stopped(), "Timer fails to track stopped status");
WaitASec();
std::cout << " Check time for 2sec" << std::endl;
CheckTime(timer, 2.0);
std::cout << " Stop the timer" << std::endl;
timer.Stop();
VTKM_TEST_ASSERT(timer.Stopped(), "Timer fails to track stopped status");
#ifdef VTKM_WINDOWS
Sleep(1000);
#else
sleep(1);
#endif
std::cout << "Elapsed time measured by any Tag with an early stop: " << elapsedTime << std::endl;
VTKM_TEST_ASSERT(elapsedTime > 1.999,
"General Timer did not capture two full seconds wait in second launch.");
VTKM_TEST_ASSERT(elapsedTime < 3.0,
"General Timer counted too far or system really busy in second launch.");
std::cout << " Check time for 2sec" << std::endl;
CheckTime(timer, 2.0);
WaitASec();
std::cout << " Check that timer legitimately stopped at 2sec" << std::endl;
CheckTime(timer, 2.0);
}
struct TimerCheckFunctor
{
void operator()(vtkm::cont::DeviceAdapterId device) const
{
if ((device != vtkm::cont::DeviceAdapterTagAny()) &&
!vtkm::cont::GetGlobalRuntimeDeviceTracker().CanRunOn(device))
{
// A timer will not work if set on a device that is not supported. Just skip this test.
return;
}
{
std::cout << "Checking Timer on device " << device.GetName() << " set with constructor"
<< std::endl;
vtkm::cont::Timer timer(device);
DoTimerCheck(timer);
}
{
std::cout << "Checking Timer on device " << device.GetName() << " reset" << std::endl;
vtkm::cont::Timer timer;
timer.Reset(device);
DoTimerCheck(timer);
}
}
};
void DoTimerTest()
{
std::cout << "Check default timer" << std::endl;
vtkm::cont::Timer timer;
DoTimerCheck(timer);
vtkm::ListForEach(TimerCheckFunctor(), TimerTestDevices());
}
} // anonymous namespace
int UnitTestTimer(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(Time, argc, argv);
return vtkm::cont::testing::Testing::Run(DoTimerTest, argc, argv);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment