2.11. Timers

It is often the case that you need to measure the time it takes for an operation to happen. This could be for performing measurements for algorithm study or it could be to dynamically adjust scheduling.

Performing timing in a multi-threaded environment can be tricky because operations happen asynchronously. To ensure that accurate timings can be made, VTK‑m provides a vtkm::cont::Timer class to provide an accurate measurement of operations that happen on devices that VTK‑m can use. By default, vtkm::cont::Timer will time operations on all possible devices.

The timer is started by calling the vtkm::cont::Timer::Start() method. The timer can subsequently be stopped by calling vtkm::cont::Timer::Stop(). The time elapsed between calls to vtkm::cont::Timer::Start() and vtkm::cont::Timer::Stop() (or the current time if vtkm::cont::Timer::Stop() was not called) can be retrieved with a call to the vtkm::cont::Timer::GetElapsedTime() method. Subsequently calling vtkm::cont::Timer::Start() again will restart the timer.

Example 2.63 Using vtkm::cont::Timer.
 1  vtkm::filter::field_transform::PointElevation elevationFilter;
 2  elevationFilter.SetUseCoordinateSystemAsField(true);
 3  elevationFilter.SetOutputFieldName("elevation");
 4
 5  vtkm::cont::Timer timer;
 6
 7  timer.Start();
 8
 9  vtkm::cont::DataSet result = elevationFilter.Execute(dataSet);
10
11  // This code makes sure data is pulled back to the host in a host/device
12  // architecture.
13  vtkm::cont::ArrayHandle<vtkm::Float64> outArray;
14  result.GetField("elevation").GetData().AsArrayHandle(outArray);
15  outArray.SyncControlArray();
16
17  timer.Stop();
18
19  vtkm::Float64 elapsedTime = timer.GetElapsedTime();
20
21  std::cout << "Time to run: " << elapsedTime << std::endl;

Common Errors

Some device require data to be copied between the host CPU and the device. In this case you might want to measure the time to copy data back to the host. This can be done by “touching” the data on the host by getting a control portal.

The VTK‑m vtkm::cont::Timer does its best to capture the time it takes for all parallel operations run between calls to vtkm::cont::Timer::Start() and vtkm::cont::Timer::Stop() to complete. It does so by synchronizing to concurrent execution on devices that might be in use.

Common Errors

Because vtkm::cont::Timer synchronizes with devices (essentially waiting for the device to finish executing), that can have an effect on how your program runs. Be aware that using a vtkm::cont::Timer can itself change the performance of your code. In particular, starting and stopping the timer many times to measure the parts of a sequence of operations can potentially make the whole operation run slower.

By default, vtkm::cont::Timer will synchronize with all active devices. However, if you want to measure the time for a specific device, then you can pass the device adapter tag or id to vtkm::cont::Timer’s constructor. You can also change the device being used by passing a device adapter tag or id to the vtkm::cont::Timer::Reset() method. A device can also be specified through an optional argument to the vtkm::cont::Timer::GetElapsedTime() method.

class Timer

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 time which matches the one being used to execute algorithms to ensure that thread synchronization is correct and accurate. If no device adapter is provided at construction time, the maximum elapsed time of all enabled deivces will be returned. Normally cuda is expected to 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.

Public Functions

void Reset()

Restores the initial state of the :class:vtkm::cont::Timer.

All previous recorded time is erased. Reset() optionally takes a device adapter tag or id that specifies on which device to time and synchronize.

void Reset(vtkm::cont::DeviceAdapterId device)

Resets the timer and changes the device to time on.

void Start()

Causes the Timer to begin timing.

The elapsed time will record an interval beginning when this method is called.

void Stop()

Causes the Timer() to finish timing.

The elapsed time will record an interval ending when this method is called. It is invalid to stop the timer if Started() is not true.

bool Started() const

Returns true if Start() has been called.

It is invalid to try to get the elapsed time if Started() is not true.

bool Stopped() const

Returns true if Timer::Stop() has been called.

If Stopped() is true, then the elapsed time will no longer increase. If Stopped() is false and Started() is true, then the timer is still running.

bool Ready() const

Used to check if Timer has finished the synchronization to get the result from the device.

vtkm::Float64 GetElapsedTime() const

Returns the amount of time that has elapsed between calling Start() and Stop().

If Stop() was not called, then the amount of time between calling Start() and GetElapsedTime() is returned. GetElapsedTime() can optionally take a device adapter tag or id to specify for which device to return the elapsed time. Returns the device for which this timer is synchronized. If the device adapter has the same id as vtkm::cont::DeviceAdapterTagAny, then the timer will synchronize all devices.

inline vtkm::cont::DeviceAdapterId GetDevice() const

Returns the id of the device adapter for which this timer is synchronized.

If the device adapter has the same id as vtkm::cont::DeviceAdapterTagAny (the default), then the timer will synchronize on all devices.

void Synchronize() const

Synchronize the device(s) that this timer is monitoring without starting or stopping the timer.

This is useful for ensuring that external events are synchronized to this timer.

Note that this method will allways block until the device(s) finish even if the Start/Stop methods do not actually block. For example, the timer for CUDA does not actually wait for asynchronous operations to finish. Rather, it inserts a fence and records the time as fences are encounted. But regardless, this Synchronize method will block for the CUDA device.