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.
This commit is contained in:
Robert Maynard 2017-11-07 14:17:28 -05:00
parent cc71e8ec6e
commit 68381d9fe5
5 changed files with 148 additions and 118 deletions

@ -110,6 +110,7 @@ set(device_sources
ArrayRangeCompute.cxx
CellSetExplicit.cxx
RuntimeDeviceTracker.cxx
TryExecute.cxx
)
vtkm_declare_headers(${headers})

@ -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
///

77
vtkm/cont/TryExecute.cxx Normal file

@ -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 <vtkm/cont/ErrorBadAllocation.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/TryExecute.h>
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;
}
}
}
}
}

@ -21,10 +21,6 @@
#define vtk_m_cont_TryExecute_h
#include <vtkm/cont/DeviceAdapterListTag.h>
#include <vtkm/cont/ErrorBadAllocation.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
namespace vtkm
@ -35,105 +31,56 @@ namespace cont
namespace detail
{
template <typename Functor, typename Device, bool DeviceAdapterValid>
struct TryExecuteRunIfValid;
VTKM_CONT_EXPORT void HandleTryExecuteException(vtkm::Int8,
const std::string&,
vtkm::cont::RuntimeDeviceTracker&);
template <typename Functor, typename Device>
struct TryExecuteRunIfValid<Functor, Device, false>
template <typename DeviceTag, typename Functor>
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 <typename Functor, typename Device>
struct TryExecuteRunIfValid<Functor, Device, true>
{
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<DeviceTag>;
HandleTryExecuteException(Traits::GetId(), Traits::GetName(), tracker);
}
// If we are here, then the functor was either never run or failed.
return false;
}
};
template <typename FunctorType>
// If we are here, then the functor was either never run or failed.
return false;
}
template <typename DeviceTag, typename Functor>
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 <typename DeviceTag, typename Functor>
void operator()(DeviceTag tag,
Functor&& f,
vtkm::cont::RuntimeDeviceTracker& tracker,
bool& ran) const
{
}
template <typename Device>
VTKM_CONT bool operator()(Device)
{
if (!this->Success)
if (!ran)
{
using DeviceTraits = vtkm::cont::DeviceAdapterTraits<Device>;
this->Success = detail::TryExecuteRunIfValid<FunctorType, Device, DeviceTraits::Valid>::Run(
this->Functor, this->Tracker);
using DeviceTraits = vtkm::cont::DeviceAdapterTraits<DeviceTag>;
ran = TryExecuteIfValid(std::integral_constant<bool, DeviceTraits::Valid>(),
tag,
std::forward<Functor>(f),
tracker);
}
return this->Success;
}
private:
void operator=(const TryExecuteImpl<FunctorType>&) = delete;
};
} // namespace detail
@ -160,44 +107,25 @@ private:
/// is used.
///
template <typename Functor, typename DeviceList>
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<const Functor> internals(functor, tracker);
vtkm::ListForEach(internals, DeviceList());
return internals.Success;
bool success = false;
detail::TryExecuteImpl task;
vtkm::ListForEach(task, DeviceList(), std::forward<Functor>(functor), tracker, success);
return success;
}
template <typename Functor, typename DeviceList>
VTKM_CONT bool TryExecute(Functor& functor, vtkm::cont::RuntimeDeviceTracker tracker, DeviceList)
{
detail::TryExecuteImpl<Functor> internals(functor, tracker);
vtkm::ListForEach(internals, DeviceList());
return internals.Success;
}
template <typename Functor, typename DeviceList>
VTKM_CONT bool TryExecute(const Functor& functor, DeviceList)
{
return vtkm::cont::TryExecute(functor, vtkm::cont::GetGlobalRuntimeDeviceTracker(), DeviceList());
}
template <typename Functor, typename DeviceList>
VTKM_CONT bool TryExecute(Functor& functor, DeviceList)
VTKM_CONT bool TryExecute(Functor&& functor, DeviceList)
{
return vtkm::cont::TryExecute(functor, vtkm::cont::GetGlobalRuntimeDeviceTracker(), DeviceList());
}
template <typename Functor>
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 <typename Functor>
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<decltype(tracker)>(tracker), VTKM_DEFAULT_DEVICE_ADAPTER_LIST_TAG());
}
}
} // namespace vtkm::cont

@ -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<vtkm::FloatDefault> 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()