From 68381d9fe53e00c1df34b9dbbcfc2fec55478fb7 Mon Sep 17 00:00:00 2001 From: Robert Maynard Date: Tue, 7 Nov 2017 14:17:28 -0500 Subject: [PATCH] Rework TryExecute to leverage perfect forwarding. By using perfect forwarding we can reduce not only the amount of TryExecute signatures, but we can enable the ability to pass temporary functors to TryExecute. At the same time we have optimized TryExecute by moving the string generation code into a single function that is compiled into the vtkm_cont library. The end result is that the vtkm_rendering library size has been reduced from 12MB to 11MB, and we shave off about 5% of our build time. --- vtkm/cont/CMakeLists.txt | 1 + vtkm/cont/RuntimeDeviceTracker.h | 11 ++ vtkm/cont/TryExecute.cxx | 77 +++++++++++ vtkm/cont/TryExecute.h | 164 +++++++---------------- vtkm/cont/testing/UnitTestTryExecute.cxx | 13 ++ 5 files changed, 148 insertions(+), 118 deletions(-) create mode 100644 vtkm/cont/TryExecute.cxx diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index 69cf2159e..95c140400 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -110,6 +110,7 @@ set(device_sources ArrayRangeCompute.cxx CellSetExplicit.cxx RuntimeDeviceTracker.cxx + TryExecute.cxx ) vtkm_declare_headers(${headers}) diff --git a/vtkm/cont/RuntimeDeviceTracker.h b/vtkm/cont/RuntimeDeviceTracker.h index 3b355b756..cf0f4ad8d 100644 --- a/vtkm/cont/RuntimeDeviceTracker.h +++ b/vtkm/cont/RuntimeDeviceTracker.h @@ -74,6 +74,17 @@ public: this->SetDeviceState(Traits::GetId(), Traits::GetName(), false); } + /// Report a failure to allocate memory on a device, this will flag the + /// device as being unusable for all future invocations of the instance of + /// the filter. + /// + VTKM_CONT void ReportAllocationFailure(vtkm::Int8 deviceId, + const std::string& name, + const vtkm::cont::ErrorBadAllocation&) + { + this->SetDeviceState(deviceId, name, false); + } + /// Reset the tracker for the given device. This will discard any updates /// caused by reported failures /// diff --git a/vtkm/cont/TryExecute.cxx b/vtkm/cont/TryExecute.cxx new file mode 100644 index 000000000..a2b1fe66c --- /dev/null +++ b/vtkm/cont/TryExecute.cxx @@ -0,0 +1,77 @@ +//============================================================================ +// 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 2016 National Technology & Engineering Solutions of Sandia, LLC (NTESS). +// Copyright 2016 UT-Battelle, LLC. +// Copyright 2016 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 +#include +#include +#include + +namespace vtkm +{ +namespace cont +{ +namespace detail +{ + +void HandleTryExecuteException(vtkm::Int8 deviceId, + const std::string& name, + vtkm::cont::RuntimeDeviceTracker& tracker) +{ + try + { + //re-throw the last exception + throw; + } + catch (vtkm::cont::ErrorBadAllocation& e) + { + std::cerr << "caught ErrorBadAllocation " << e.GetMessage() << std::endl; + //currently we only consider OOM errors worth disabling a device for + //than we fallback to another device + tracker.ReportAllocationFailure(deviceId, name, e); + } + catch (vtkm::cont::ErrorBadType& e) + { + //should bad type errors should stop the execution, instead of + //deferring to another device adapter? + std::cerr << "caught ErrorBadType : " << e.GetMessage() << std::endl; + } + catch (vtkm::cont::ErrorBadValue& e) + { + //should bad value errors should stop the filter, instead of deferring + //to another device adapter? + std::cerr << "caught ErrorBadValue : " << e.GetMessage() << std::endl; + } + catch (vtkm::cont::Error& e) + { + //general errors should be caught and let us try the next device adapter. + std::cerr << "exception is: " << e.GetMessage() << std::endl; + } + catch (std::exception& e) + { + std::cerr << "caught standard exception: " << e.what() << std::endl; + } + catch (...) + { + std::cerr << "unknown exception caught" << std::endl; + } +} +} +} +} diff --git a/vtkm/cont/TryExecute.h b/vtkm/cont/TryExecute.h index 702a7086a..3837ef891 100644 --- a/vtkm/cont/TryExecute.h +++ b/vtkm/cont/TryExecute.h @@ -21,10 +21,6 @@ #define vtk_m_cont_TryExecute_h #include -#include -#include -#include - #include namespace vtkm @@ -35,105 +31,56 @@ namespace cont namespace detail { -template -struct TryExecuteRunIfValid; +VTKM_CONT_EXPORT void HandleTryExecuteException(vtkm::Int8, + const std::string&, + vtkm::cont::RuntimeDeviceTracker&); -template -struct TryExecuteRunIfValid +template +bool TryExecuteIfValid(std::true_type, + DeviceTag tag, + Functor&& f, + vtkm::cont::RuntimeDeviceTracker& tracker) { - VTKM_CONT - static bool Run(Functor&, const vtkm::cont::RuntimeDeviceTracker&) { return false; } -}; - -template -struct TryExecuteRunIfValid -{ - VTKM_IS_DEVICE_ADAPTER_TAG(Device); - - VTKM_CONT - static bool Run(Functor& functor, vtkm::cont::RuntimeDeviceTracker tracker) + if (tracker.CanRunOn(tag)) { - if (tracker.CanRunOn(Device())) + try { - try - { - return functor(Device()); - } - catch (vtkm::cont::ErrorBadAllocation& e) - { - std::cerr << "caught ErrorBadAllocation " << e.GetMessage() << std::endl; - //currently we only consider OOM errors worth disabling a device for - //than we fallback to another device - tracker.ReportAllocationFailure(Device(), e); - } - catch (vtkm::cont::ErrorBadType& e) - { - //should bad type errors should stop the execution, instead of - //deferring to another device adapter? - std::cerr << "caught ErrorBadType : " << e.GetMessage() << std::endl; - } - catch (vtkm::cont::ErrorBadValue& e) - { - //should bad value errors should stop the filter, instead of deferring - //to another device adapter? - std::cerr << "caught ErrorBadValue : " << e.GetMessage() << std::endl; - } - catch (vtkm::cont::Error& e) - { - //general errors should be caught and let us try the next device adapter. - std::cerr << "exception is: " << e.GetMessage() << std::endl; - } - catch (std::exception& e) - { - std::cerr << "caught standard exception: " << e.what() << std::endl; - } - catch (...) - { - std::cerr << "unknown exception caught" << std::endl; - } + return f(tag); + } + catch (...) + { + using Traits = vtkm::cont::DeviceAdapterTraits; + HandleTryExecuteException(Traits::GetId(), Traits::GetName(), tracker); } - - // If we are here, then the functor was either never run or failed. - return false; } -}; -template + // If we are here, then the functor was either never run or failed. + return false; +} + +template +bool TryExecuteIfValid(std::false_type, DeviceTag, Functor&&, vtkm::cont::RuntimeDeviceTracker&) +{ + return false; +} + struct TryExecuteImpl { - // Warning, these are a references. Make sure referenced objects do not go - // out of scope. - FunctorType& Functor; - vtkm::cont::RuntimeDeviceTracker Tracker; - - bool Success; - - VTKM_CONT - TryExecuteImpl( - FunctorType& functor, - vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker()) - : Functor(functor) - , Tracker(tracker) - , Success(false) + template + void operator()(DeviceTag tag, + Functor&& f, + vtkm::cont::RuntimeDeviceTracker& tracker, + bool& ran) const { - } - - template - VTKM_CONT bool operator()(Device) - { - if (!this->Success) + if (!ran) { - using DeviceTraits = vtkm::cont::DeviceAdapterTraits; - - this->Success = detail::TryExecuteRunIfValid::Run( - this->Functor, this->Tracker); + using DeviceTraits = vtkm::cont::DeviceAdapterTraits; + ran = TryExecuteIfValid(std::integral_constant(), + tag, + std::forward(f), + tracker); } - - return this->Success; } - -private: - void operator=(const TryExecuteImpl&) = delete; }; } // namespace detail @@ -160,44 +107,25 @@ private: /// is used. /// template -VTKM_CONT bool TryExecute(const Functor& functor, - vtkm::cont::RuntimeDeviceTracker tracker, - DeviceList) +VTKM_CONT bool TryExecute(Functor&& functor, vtkm::cont::RuntimeDeviceTracker tracker, DeviceList) { - detail::TryExecuteImpl internals(functor, tracker); - vtkm::ListForEach(internals, DeviceList()); - return internals.Success; + bool success = false; + detail::TryExecuteImpl task; + vtkm::ListForEach(task, DeviceList(), std::forward(functor), tracker, success); + return success; } template -VTKM_CONT bool TryExecute(Functor& functor, vtkm::cont::RuntimeDeviceTracker tracker, DeviceList) -{ - detail::TryExecuteImpl internals(functor, tracker); - vtkm::ListForEach(internals, DeviceList()); - return internals.Success; -} -template -VTKM_CONT bool TryExecute(const Functor& functor, DeviceList) -{ - return vtkm::cont::TryExecute(functor, vtkm::cont::GetGlobalRuntimeDeviceTracker(), DeviceList()); -} -template -VTKM_CONT bool TryExecute(Functor& functor, DeviceList) +VTKM_CONT bool TryExecute(Functor&& functor, DeviceList) { return vtkm::cont::TryExecute(functor, vtkm::cont::GetGlobalRuntimeDeviceTracker(), DeviceList()); } template VTKM_CONT bool TryExecute( - const Functor& functor, + Functor&& functor, vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker()) { - return vtkm::cont::TryExecute(functor, tracker, VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG()); -} -template -VTKM_CONT bool TryExecute( - Functor& functor, - vtkm::cont::RuntimeDeviceTracker tracker = vtkm::cont::GetGlobalRuntimeDeviceTracker()) -{ - return vtkm::cont::TryExecute(functor, tracker, VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG()); + return vtkm::cont::TryExecute( + functor, std::forward(tracker), VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG()); } } } // namespace vtkm::cont diff --git a/vtkm/cont/testing/UnitTestTryExecute.cxx b/vtkm/cont/testing/UnitTestTryExecute.cxx index c00788db4..018eaee72 100644 --- a/vtkm/cont/testing/UnitTestTryExecute.cxx +++ b/vtkm/cont/testing/UnitTestTryExecute.cxx @@ -81,6 +81,19 @@ void TryExecuteWithList(DeviceList, bool expectSuccess) { VTKM_TEST_ASSERT(!result, "Call returned true when expected failure."); } + + //verify the ability to pass rvalue functors + vtkm::cont::ArrayHandle outArray2; + result = vtkm::cont::TryExecute(TryExecuteTestFunctor(inArray, outArray2), DeviceList()); + if (expectSuccess) + { + VTKM_TEST_ASSERT(result, "Call returned failure when expected success."); + CheckPortal(outArray2.GetPortalConstControl()); + } + else + { + VTKM_TEST_ASSERT(!result, "Call returned true when expected failure."); + } } static void Run()