//============================================================================ // 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 #include #include #include #include #include #include namespace { template using DeviceInvalid = std::integral_constant; using EnabledDeviceList = vtkm::ListRemoveIf; template using DeviceTimerPtr = std::unique_ptr>; using EnabledTimerImpls = vtkm::ListTransform; using EnabledTimerImplTuple = vtkm::ListApply; // 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 struct Index; template class Container, class... Types> struct Index> { static const std::size_t value = 0; }; template class Container, class... Types> struct Index> { static const std::size_t value = 1 + Index>::value; }; template VTKM_CONT inline typename std::tuple_element::value, EnabledTimerImplTuple>::type& GetUniqueTimerPtr(Device, EnabledTimerImplTuple& enabledTimers) { return std::get::value>(enabledTimers); } struct InitFunctor { template VTKM_CONT void operator()(Device, EnabledTimerImplTuple& timerImpls) { //We don't use the runtime device tracker to very initializtion support //so that the following use case is supported: // // GetRuntimeDeviceTracker().Disable( openMP ); // vtkm::cont::Timer timer; //tracks all active devices // GetRuntimeDeviceTracker().Enable( openMP ); // timer.Start() //want to test openmp // // timer.GetElapsedTime() // // When `GetElapsedTime` is called we need to make sure that the OpenMP // device timer is safe to call. At the same time we still need to make // sure that we have the required runtime and not just compile time support // this is why we use `DeviceAdapterRuntimeDetector` bool haveRequiredRuntimeSupport = vtkm::cont::DeviceAdapterRuntimeDetector{}.Exists(); if (haveRequiredRuntimeSupport) { std::get::value>(timerImpls) .reset(new vtkm::cont::DeviceAdapterTimerImplementation()); } } }; struct ResetFunctor { template VTKM_CONT void operator()(Device device, vtkm::cont::DeviceAdapterId deviceToRunOn, const vtkm::cont::RuntimeDeviceTracker& tracker, EnabledTimerImplTuple& timerImpls) { if ((deviceToRunOn == device || deviceToRunOn == vtkm::cont::DeviceAdapterTagAny()) && tracker.CanRunOn(device)) { GetUniqueTimerPtr(device, timerImpls)->Reset(); } } }; struct StartFunctor { template VTKM_CONT void operator()(Device device, vtkm::cont::DeviceAdapterId deviceToRunOn, const vtkm::cont::RuntimeDeviceTracker& tracker, EnabledTimerImplTuple& timerImpls) { if ((deviceToRunOn == device || deviceToRunOn == vtkm::cont::DeviceAdapterTagAny()) && tracker.CanRunOn(device)) { GetUniqueTimerPtr(device, timerImpls)->Start(); } } }; struct StopFunctor { template VTKM_CONT void operator()(Device device, vtkm::cont::DeviceAdapterId deviceToRunOn, const vtkm::cont::RuntimeDeviceTracker& tracker, EnabledTimerImplTuple& timerImpls) { if ((deviceToRunOn == device || deviceToRunOn == vtkm::cont::DeviceAdapterTagAny()) && tracker.CanRunOn(device)) { GetUniqueTimerPtr(device, timerImpls)->Stop(); } } }; struct StartedFunctor { bool Value = true; template VTKM_CONT void operator()(Device device, vtkm::cont::DeviceAdapterId deviceToRunOn, const vtkm::cont::RuntimeDeviceTracker& tracker, EnabledTimerImplTuple& timerImpls) { if ((deviceToRunOn == device || deviceToRunOn == vtkm::cont::DeviceAdapterTagAny()) && tracker.CanRunOn(device)) { this->Value &= GetUniqueTimerPtr(device, timerImpls)->Started(); } } }; struct StoppedFunctor { bool Value = true; template VTKM_CONT void operator()(Device device, vtkm::cont::DeviceAdapterId deviceToRunOn, const vtkm::cont::RuntimeDeviceTracker& tracker, EnabledTimerImplTuple& timerImpls) { if ((deviceToRunOn == device || deviceToRunOn == vtkm::cont::DeviceAdapterTagAny()) && tracker.CanRunOn(device)) { this->Value &= GetUniqueTimerPtr(device, timerImpls)->Stopped(); } } }; struct ReadyFunctor { bool Value = true; template VTKM_CONT void operator()(Device device, vtkm::cont::DeviceAdapterId deviceToRunOn, const vtkm::cont::RuntimeDeviceTracker& tracker, EnabledTimerImplTuple& timerImpls) { if ((deviceToRunOn == device || deviceToRunOn == vtkm::cont::DeviceAdapterTagAny()) && tracker.CanRunOn(device)) { this->Value &= GetUniqueTimerPtr(device, timerImpls)->Ready(); } } }; struct ElapsedTimeFunctor { vtkm::Float64 ElapsedTime = 0.0; template VTKM_CONT void operator()(Device device, vtkm::cont::DeviceAdapterId deviceToRunOn, const vtkm::cont::RuntimeDeviceTracker& tracker, EnabledTimerImplTuple& timerImpls) { if ((deviceToRunOn == device || deviceToRunOn == vtkm::cont::DeviceAdapterTagAny()) && tracker.CanRunOn(device)) { this->ElapsedTime = vtkm::Max(this->ElapsedTime, GetUniqueTimerPtr(device, timerImpls)->GetElapsedTime()); } } }; } // anonymous namespace namespace vtkm { namespace cont { namespace detail { struct EnabledDeviceTimerImpls { EnabledDeviceTimerImpls() { vtkm::ListForEach(InitFunctor(), EnabledDeviceList(), this->EnabledTimers); } ~EnabledDeviceTimerImpls() {} // A tuple of enabled timer implementations EnabledTimerImplTuple EnabledTimers; }; } } } // namespace vtkm::cont::detail namespace vtkm { namespace cont { Timer::Timer() : Device(vtkm::cont::DeviceAdapterTagAny()) , Internal(new detail::EnabledDeviceTimerImpls) { } Timer::Timer(vtkm::cont::DeviceAdapterId device) : Device(device) , Internal(new detail::EnabledDeviceTimerImpls) { const auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); if (!tracker.CanRunOn(device)) { VTKM_LOG_S(vtkm::cont::LogLevel::Error, "Device '" << device.GetName() << "' can not run on current Device." "Thus timer is not usable"); } } Timer::~Timer() = default; void Timer::Reset() { const auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); vtkm::ListForEach( ResetFunctor(), EnabledDeviceList(), this->Device, tracker, this->Internal->EnabledTimers); } void Timer::Reset(vtkm::cont::DeviceAdapterId device) { const auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); if (!tracker.CanRunOn(device)) { VTKM_LOG_S(vtkm::cont::LogLevel::Error, "Device '" << device.GetName() << "' can not run on current Device." "Thus timer is not usable"); } this->Device = device; this->Reset(); } void Timer::Start() { const auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); vtkm::ListForEach( StartFunctor(), EnabledDeviceList(), this->Device, tracker, this->Internal->EnabledTimers); } void Timer::Stop() { const auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); vtkm::ListForEach( StopFunctor(), EnabledDeviceList(), this->Device, tracker, this->Internal->EnabledTimers); } bool Timer::Started() const { const auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); StartedFunctor functor; vtkm::ListForEach( functor, EnabledDeviceList(), this->Device, tracker, this->Internal->EnabledTimers); return functor.Value; } bool Timer::Stopped() const { const auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); StoppedFunctor functor; vtkm::ListForEach( functor, EnabledDeviceList(), this->Device, tracker, this->Internal->EnabledTimers); return functor.Value; } bool Timer::Ready() const { const auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); ReadyFunctor functor; vtkm::ListForEach( functor, EnabledDeviceList(), this->Device, tracker, this->Internal->EnabledTimers); return functor.Value; } vtkm::Float64 Timer::GetElapsedTime() const { //Throw an exception if a timer bound device now can't be used auto& tracker = vtkm::cont::GetRuntimeDeviceTracker(); ElapsedTimeFunctor functor; vtkm::ListForEach( functor, EnabledDeviceList(), this->Device, tracker, this->Internal->EnabledTimers); return functor.ElapsedTime; } void Timer::Synchronize() const { vtkm::cont::Algorithm::Synchronize(this->Device); } } } // namespace vtkm::cont