Merge topic 'internally_improve_CastAndCall_TryExecute'

dd25c5c2 DynamicCellSet CastAndCall refactored to use the new vtkm::ForEach
c9f1d192 DynamicArrayHandle CastAndCall refactored to use the new vtkm::ForEach
68381d9f Rework TryExecute to leverage perfect forwarding.

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Kenneth Moreland <kmorel@sandia.gov>
Merge-request: !1003
This commit is contained in:
Robert Maynard 2017-11-08 22:44:10 +00:00 committed by Kitware Robot
commit 713e428abf
7 changed files with 181 additions and 167 deletions

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

@ -132,9 +132,8 @@ template <typename Type, typename Storage>
VTKM_CONT vtkm::cont::ArrayHandle<Type, Storage>* DynamicArrayHandleTryCast(
vtkm::cont::detail::PolymorphicArrayHandleContainerBase* arrayContainer)
{
vtkm::cont::detail::PolymorphicArrayHandleContainer<Type, Storage>* downcastContainer =
dynamic_cast<vtkm::cont::detail::PolymorphicArrayHandleContainer<Type, Storage>*>(
arrayContainer);
vtkm::cont::detail::PolymorphicArrayHandleContainer<Type, Storage>* downcastContainer = nullptr;
downcastContainer = dynamic_cast<decltype(downcastContainer)>(arrayContainer);
if (downcastContainer != nullptr)
{
return &downcastContainer->Array;
@ -407,13 +406,10 @@ using DynamicArrayHandle =
namespace detail
{
template <typename Functor>
struct ListFunctorWrapper
struct DynamicArrayHandleTry
{
ListFunctorWrapper(bool& called, const Functor& f, PolymorphicArrayHandleContainerBase* c)
: Called(called)
, Container(c)
, Function(f)
DynamicArrayHandleTry(const PolymorphicArrayHandleContainerBase* const c)
: Container(c)
{
}
@ -425,16 +421,17 @@ struct ListFunctorWrapper
this->run(std::forward<decltype(p)>(p), invalid{}, args...);
}
template <typename T, typename U, typename... Args>
void run(std::pair<T, U>&&, std::false_type, Args&&... args) const
template <typename T, typename U, typename Functor, typename... Args>
void run(std::pair<T, U>&&, std::false_type, Functor&& f, bool& called, Args&&... args) const
{
if (!this->Called)
if (!called)
{
vtkm::cont::ArrayHandle<T, U>* handle = DynamicArrayHandleTryCast<T, U>(this->Container);
if (handle)
using downcastType = const vtkm::cont::detail::PolymorphicArrayHandleContainer<T, U>* const;
downcastType downcastContainer = dynamic_cast<downcastType>(this->Container);
if (downcastContainer)
{
this->Function(*handle, std::forward<Args>(args)...);
this->Called = true;
f(downcastContainer->Array, std::forward<Args>(args)...);
called = true;
}
}
}
@ -444,9 +441,7 @@ struct ListFunctorWrapper
{
}
bool& Called;
PolymorphicArrayHandleContainerBase* Container;
const Functor& Function;
const PolymorphicArrayHandleContainerBase* const Container;
};
VTKM_CONT_EXPORT void ThrowCastAndCallException(PolymorphicArrayHandleContainerBase*,
@ -462,11 +457,9 @@ VTKM_CONT void DynamicArrayHandleBase<TypeList, StorageList>::CastAndCall(const
//and make it extern
using crossProduct = typename vtkm::ListCrossProduct<TypeList, StorageList>;
auto* ptr = this->ArrayContainer.get();
bool called = false;
auto task = detail::ListFunctorWrapper<Functor>(called, f, ptr);
vtkm::ListForEach(task, crossProduct{});
auto* ptr = this->ArrayContainer.get();
vtkm::ListForEach(detail::DynamicArrayHandleTry(ptr), crossProduct{}, f, called);
if (!called)
{
// throw an exception

@ -272,38 +272,30 @@ private:
namespace detail
{
template <typename Functor>
struct DynamicCellSetTryCellSet
struct DynamicCellSetTry
{
vtkm::cont::internal::SimplePolymorphicContainerBase* CellSetContainer;
const Functor& Function;
bool FoundCast;
VTKM_CONT
DynamicCellSetTryCellSet(vtkm::cont::internal::SimplePolymorphicContainerBase* cellSetContainer,
const Functor& f)
: CellSetContainer(cellSetContainer)
, Function(f)
, FoundCast(false)
DynamicCellSetTry(
const vtkm::cont::internal::SimplePolymorphicContainerBase* const cellSetContainer)
: Container(cellSetContainer)
{
}
template <typename CellSetType>
VTKM_CONT void operator()(CellSetType)
template <typename CellSetType, typename Functor>
void operator()(CellSetType, Functor&& f, bool& called) const
{
if (!this->FoundCast)
using downcastType = const vtkm::cont::internal::SimplePolymorphicContainer<CellSetType>* const;
if (!called)
{
CellSetType* cellSet = detail::DynamicCellSetTryCast<CellSetType>(this->CellSetContainer);
if (cellSet != nullptr)
downcastType downcastContainer = dynamic_cast<downcastType>(this->Container);
if (downcastContainer)
{
this->Function(*cellSet);
this->FoundCast = true;
f(downcastContainer->Item);
called = true;
}
}
}
private:
void operator=(const DynamicCellSetTryCellSet<Functor>&) = delete;
const vtkm::cont::internal::SimplePolymorphicContainerBase* const Container;
};
} // namespace detail
@ -312,11 +304,10 @@ template <typename CellSetList>
template <typename Functor>
VTKM_CONT void DynamicCellSetBase<CellSetList>::CastAndCall(const Functor& f) const
{
using TryCellSetType = detail::DynamicCellSetTryCellSet<Functor>;
TryCellSetType tryCellSet = TryCellSetType(this->CellSetContainer.get(), f);
vtkm::ListForEach(tryCellSet, CellSetList());
if (!tryCellSet.FoundCast)
bool called = false;
detail::DynamicCellSetTry tryCellSet(this->CellSetContainer.get());
vtkm::ListForEach(tryCellSet, CellSetList{}, f, called);
if (!called)
{
throw vtkm::cont::ErrorBadValue("Could not find appropriate cast for cell set.");
}

@ -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()