Merge topic 'vtkm-abort'

42c6959be Add Abort execution feature

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !2974
This commit is contained in:
Sujin Philip 2023-02-13 17:40:45 +00:00 committed by Kitware Robot
commit d05afa72f5
10 changed files with 296 additions and 95 deletions

@ -0,0 +1,22 @@
# Add initial support for aborting execution
VTK-m now has preliminary support for aborting execution. The per-thread instances of
`RuntimeDeviceTracker` have a functor called `AbortChecker`. This functor can be set using
`RuntimeDeviceTracker::SetAbortChecker()` and cleared by `RuntimeDeviceTracker::ClearAbortChecker()`
The abort checker functor should return `true` if an abort is requested for the thread,
otherwise, it should return `false`.
Before launching a new task, `TaskExecute` calls the functor to see if an abort is requested,
and If so, throws an exception of type `vtkm::cont::ErrorUserAbort`.
Any code that wants to use the abort feature, should set an appropriate `AbortChecker`
functor for the target thread. Then any piece of code that has parts that can execute on
the device should be put under a `try-catch` block. Any clean-up that is required for an
aborted execution should be handled in a `catch` block that handles exceptions of type
`vtkm::cont::ErrorUserAbort`.
The limitation of this implementation is that it is control-side only. The check for abort
is done before launching a new device task. Once execution has begun on the device, there is
currently no way to abort that. Therefore, this feature is only useful for aborting code
that is made up of several smaller device task launches (Which is the case for most
worklets and filters in VTK-m)

@ -94,6 +94,7 @@ set(headers
ErrorExecution.h
ErrorFilterExecution.h
ErrorInternal.h
ErrorUserAbort.h
ExecutionAndControlObjectBase.h
ExecutionObjectBase.h
Field.h

@ -0,0 +1,42 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_cont_ErrorUserAbort_h
#define vtk_m_cont_ErrorUserAbort_h
#include <vtkm/cont/Error.h>
namespace vtkm
{
namespace cont
{
VTKM_SILENCE_WEAK_VTABLE_WARNING_START
/// This class is thrown when vtk-m detects a request for aborting execution
/// in the current thread
///
class VTKM_ALWAYS_EXPORT ErrorUserAbort : public Error
{
public:
ErrorUserAbort()
: Error(Message, true)
{
}
private:
static constexpr const char* Message = "User abort detected.";
};
VTKM_SILENCE_WEAK_VTABLE_WARNING_END
}
} // namespace vtkm::cont
#endif // vtk_m_cont_ErrorUserAbort_h

@ -35,6 +35,7 @@ struct RuntimeDeviceTrackerInternals
std::array<bool, VTKM_MAX_DEVICE_ADAPTER_ID> RuntimeAllowed;
bool ThreadFriendlyMemAlloc = false;
std::function<bool()> AbortChecker;
};
}
@ -186,6 +187,28 @@ VTKM_CONT void RuntimeDeviceTracker::CopyStateFrom(const vtkm::cont::RuntimeDevi
*(this->Internals) = *tracker.Internals;
}
VTKM_CONT
void RuntimeDeviceTracker::SetAbortChecker(const std::function<bool()>& func)
{
this->Internals->AbortChecker = func;
}
VTKM_CONT
bool RuntimeDeviceTracker::CheckForAbortRequest() const
{
if (this->Internals->AbortChecker)
{
return this->Internals->AbortChecker();
}
return false;
}
VTKM_CONT
void RuntimeDeviceTracker::ClearAbortChecker()
{
this->Internals->AbortChecker = nullptr;
}
VTKM_CONT
void RuntimeDeviceTracker::PrintSummary(std::ostream& out) const
{
@ -228,25 +251,6 @@ ScopedRuntimeDeviceTracker::ScopedRuntimeDeviceTracker(
VTKM_LOG_S(vtkm::cont::LogLevel::DevicesEnabled, "Entering scoped runtime region");
}
VTKM_CONT
ScopedRuntimeDeviceTracker::ScopedRuntimeDeviceTracker(vtkm::cont::DeviceAdapterId device,
RuntimeDeviceTrackerMode mode)
: ScopedRuntimeDeviceTracker(GetRuntimeDeviceTracker())
{
if (mode == RuntimeDeviceTrackerMode::Force)
{
this->ForceDevice(device);
}
else if (mode == RuntimeDeviceTrackerMode::Enable)
{
this->ResetDevice(device);
}
else if (mode == RuntimeDeviceTrackerMode::Disable)
{
this->DisableDevice(device);
}
}
VTKM_CONT
ScopedRuntimeDeviceTracker::ScopedRuntimeDeviceTracker(
vtkm::cont::DeviceAdapterId device,
@ -268,6 +272,14 @@ ScopedRuntimeDeviceTracker::ScopedRuntimeDeviceTracker(
}
}
VTKM_CONT ScopedRuntimeDeviceTracker::ScopedRuntimeDeviceTracker(
const std::function<bool()>& abortChecker,
const vtkm::cont::RuntimeDeviceTracker& tracker)
: ScopedRuntimeDeviceTracker(tracker)
{
this->SetAbortChecker(abortChecker);
}
VTKM_CONT
ScopedRuntimeDeviceTracker::~ScopedRuntimeDeviceTracker()
{

@ -17,6 +17,7 @@
#include <vtkm/cont/ErrorBadDevice.h>
#include <vtkm/cont/RuntimeDeviceInformation.h>
#include <functional>
#include <memory>
namespace vtkm
@ -123,6 +124,18 @@ public:
///
VTKM_CONT void CopyStateFrom(const vtkm::cont::RuntimeDeviceTracker& tracker);
///@{
/// \brief Set/Clear the abort checker functor.
///
/// If set the abort checker functor is called by \c TryExecute before scheduling
/// a task on a device from the associated the thread. If the functor returns
/// \e true, an exception is thrown.
VTKM_CONT void SetAbortChecker(const std::function<bool()>& func);
VTKM_CONT void ClearAbortChecker();
///@}
VTKM_CONT bool CheckForAbortRequest() const;
VTKM_CONT void PrintSummary(std::ostream& out) const;
private:
@ -149,82 +162,7 @@ private:
void LogEnabledDevices() const;
};
enum struct RuntimeDeviceTrackerMode
{
Force,
Enable,
Disable
};
/// A class that can be used to determine or modify which device adapter
/// VTK-m algorithms should be run on. This class captures the state
/// of the per-thread device adapter and will revert any changes applied
/// during its lifetime on destruction.
///
///
struct VTKM_CONT_EXPORT ScopedRuntimeDeviceTracker : public vtkm::cont::RuntimeDeviceTracker
{
/// Construct a ScopedRuntimeDeviceTracker where the state of the active devices
/// for the current thread are determined by the parameters to the constructor.
///
/// 'Force'
/// - Force-Enable the provided single device adapter
/// - Force-Enable all device adapters when using vtkm::cont::DeviceAdaterTagAny
/// 'Enable'
/// - Enable the provided single device adapter if it was previously disabled
/// - Enable all device adapters that are currently disabled when using
/// vtkm::cont::DeviceAdaterTagAny
/// 'Disable'
/// - Disable the provided single device adapter
/// - Disable all device adapters when using vtkm::cont::DeviceAdaterTagAny
///
/// Constructor is not thread safe
VTKM_CONT ScopedRuntimeDeviceTracker(
vtkm::cont::DeviceAdapterId device,
RuntimeDeviceTrackerMode mode = RuntimeDeviceTrackerMode::Force);
/// Construct a ScopedRuntimeDeviceTracker associated with the thread
/// associated with the provided tracker. The active devices
/// for the current thread are determined by the parameters to the constructor.
///
/// 'Force'
/// - Force-Enable the provided single device adapter
/// - Force-Enable all device adapters when using vtkm::cont::DeviceAdaterTagAny
/// 'Enable'
/// - Enable the provided single device adapter if it was previously disabled
/// - Enable all device adapters that are currently disabled when using
/// vtkm::cont::DeviceAdaterTagAny
/// 'Disable'
/// - Disable the provided single device adapter
/// - Disable all device adapters when using vtkm::cont::DeviceAdaterTagAny
///
/// Any modifications to the ScopedRuntimeDeviceTracker will effect what
/// ever thread the \c tracker is associated with, which might not be
/// the thread which ScopedRuntimeDeviceTracker was constructed on.
///
/// Constructor is not thread safe
VTKM_CONT ScopedRuntimeDeviceTracker(vtkm::cont::DeviceAdapterId device,
RuntimeDeviceTrackerMode mode,
const vtkm::cont::RuntimeDeviceTracker& tracker);
/// Construct a ScopedRuntimeDeviceTracker associated with the thread
/// associated with the provided tracker.
///
/// Any modifications to the ScopedRuntimeDeviceTracker will effect what
/// ever thread the \c tracker is associated with, which might not be
/// the thread which ScopedRuntimeDeviceTracker was constructed on.
///
/// Constructor is not thread safe
VTKM_CONT ScopedRuntimeDeviceTracker(const vtkm::cont::RuntimeDeviceTracker& tracker);
/// Destructor is not thread safe
VTKM_CONT ~ScopedRuntimeDeviceTracker();
private:
std::unique_ptr<detail::RuntimeDeviceTrackerInternals> SavedState;
};
///----------------------------------------------------------------------------
/// \brief Get the \c RuntimeDeviceTracker for the current thread.
///
/// Many features in VTK-m will attempt to run algorithms on the "best
@ -236,6 +174,66 @@ private:
VTKM_CONT_EXPORT
VTKM_CONT
vtkm::cont::RuntimeDeviceTracker& GetRuntimeDeviceTracker();
enum struct RuntimeDeviceTrackerMode
{
Force,
Enable,
Disable
};
///----------------------------------------------------------------------------
/// A class to create a scoped runtime device tracker object. This object captures the state
/// of the per-thread device tracker and will revert any changes applied
/// during its lifetime on destruction.
///
struct VTKM_CONT_EXPORT ScopedRuntimeDeviceTracker : public vtkm::cont::RuntimeDeviceTracker
{
/// Construct a ScopedRuntimeDeviceTracker associated with the thread,
/// associated with the provided tracker (defaults to current thread's tracker).
///
/// Any modifications to the ScopedRuntimeDeviceTracker will effect what
/// ever thread the \c tracker is associated with, which might not be
/// the thread on which the ScopedRuntimeDeviceTracker was constructed.
///
/// Constructors are not thread safe
/// @{
///
VTKM_CONT ScopedRuntimeDeviceTracker(
const vtkm::cont::RuntimeDeviceTracker& tracker = GetRuntimeDeviceTracker());
/// Use this constructor to modify the state of the device adapters associated with
/// the provided tracker. Use \p mode with \p device as follows:
///
/// 'Force' (default)
/// - Force-Enable the provided single device adapter
/// - Force-Enable all device adapters when using vtkm::cont::DeviceAdaterTagAny
/// 'Enable'
/// - Enable the provided single device adapter if it was previously disabled
/// - Enable all device adapters that are currently disabled when using
/// vtkm::cont::DeviceAdaterTagAny
/// 'Disable'
/// - Disable the provided single device adapter
/// - Disable all device adapters when using vtkm::cont::DeviceAdaterTagAny
///
VTKM_CONT ScopedRuntimeDeviceTracker(
vtkm::cont::DeviceAdapterId device,
RuntimeDeviceTrackerMode mode = RuntimeDeviceTrackerMode::Force,
const vtkm::cont::RuntimeDeviceTracker& tracker = GetRuntimeDeviceTracker());
/// Use this constructor to set the abort checker functor for the provided tracker.
///
VTKM_CONT ScopedRuntimeDeviceTracker(
const std::function<bool()>& abortChecker,
const vtkm::cont::RuntimeDeviceTracker& tracker = GetRuntimeDeviceTracker());
/// Destructor is not thread safe
VTKM_CONT ~ScopedRuntimeDeviceTracker();
private:
std::unique_ptr<detail::RuntimeDeviceTrackerInternals> SavedState;
};
}
} // namespace vtkm::cont

@ -12,6 +12,7 @@
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/ErrorFilterExecution.h>
#include <vtkm/cont/ErrorUserAbort.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
namespace vtkm
@ -55,6 +56,13 @@ VTKM_CONT_EXPORT void HandleTryExecuteException(vtkm::cont::DeviceAdapterId devi
VTKM_LOG_TRYEXECUTE_FAIL("ErrorBadValue (" << e.GetMessage() << ")", functorName, deviceId);
throw;
}
catch (vtkm::cont::ErrorUserAbort& e)
{
VTKM_LOG_S(vtkm::cont::LogLevel::Info,
e.GetMessage() << " Aborting: " << functorName << ", on device "
<< deviceId.GetName());
throw;
}
catch (vtkm::cont::Error& e)
{
VTKM_LOG_TRYEXECUTE_FAIL(e.GetMessage(), functorName, deviceId);

@ -12,6 +12,7 @@
#include <vtkm/cont/DeviceAdapterList.h>
#include <vtkm/cont/DeviceAdapterTag.h>
#include <vtkm/cont/ErrorUserAbort.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
@ -40,6 +41,11 @@ inline bool TryExecuteIfValid(std::true_type,
{
try
{
if (tracker.CheckForAbortRequest())
{
throw vtkm::cont::ErrorUserAbort{};
}
return f(tag, std::forward<Args>(args)...);
}
catch (...)

@ -122,6 +122,12 @@ if(TARGET vtkm_filter_field_conversion)
)
endif()
if(TARGET vtkm_filter_contour)
list(APPEND unit_tests_device
UnitTestAbort.cxx
)
endif()
vtkm_unit_tests(SOURCES ${unit_tests} DEVICE_SOURCES ${unit_tests_device})
#add distributed tests i.e.test to run with MPI

@ -0,0 +1,105 @@
//============================================================================
// 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 <vtkm/cont/ErrorUserAbort.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/contour/Contour.h>
#include <vtkm/source/Wavelet.h>
#include <sstream>
namespace
{
// A function that checks for abort request.
// This function will be called by `TryExecute` befaure lauching a device task
// to check if abort has been requested.
// For this test case, we are using a simple logic of returning true for the
// `abortAt`th check.
// If this test is failing, one of the things to check would be to see if the
// `Contour` filter has changed such that it no longer has atleast `abortAt`
// task invocations.
bool ShouldAbort()
{
static int abortCheckCounter = 0;
static constexpr int abortAt = 5;
if (++abortCheckCounter >= abortAt)
{
std::cout << "Abort check " << abortCheckCounter << ": true\n";
return true;
}
std::cout << "Abort check " << abortCheckCounter << ": false\n";
return false;
}
int TestAbort()
{
vtkm::source::Wavelet wavelet;
wavelet.SetExtent(vtkm::Id3(-15), vtkm::Id3(16));
auto input = wavelet.Execute();
auto range = input.GetField("RTData").GetRange().ReadPortal().Get(0);
std::vector<vtkm::Float64> isovals;
static constexpr int numDivs = 5;
for (int i = 1; i < numDivs - 1; ++i)
{
auto v = range.Min +
(static_cast<vtkm::Float64>(i) *
((range.Max - range.Min) / static_cast<vtkm::Float64>(numDivs)));
isovals.push_back(v);
}
vtkm::filter::contour::Contour contour;
contour.SetActiveField("RTData");
contour.SetIsoValues(isovals);
// First we will run the filter with the abort function set
std::cout << "Run #1 with the abort function set\n";
try
{
vtkm::cont::ScopedRuntimeDeviceTracker tracker(ShouldAbort);
auto result = contour.Execute(input);
// execution shouldn't reach here
VTKM_TEST_FAIL("Error: filter execution was not aborted. Result: ",
result.GetNumberOfPoints(),
" points and ",
result.GetNumberOfCells(),
" triangles");
}
catch (const vtkm::cont::ErrorUserAbort& e)
{
std::cout << "Execution was successfully aborted\n";
}
// Now run the filter without the abort function
std::cout << "Run #2 without the abort function set\n";
try
{
auto result = contour.Execute(input);
std::cout << "Success: filter execution was not aborted. Result: " << result.GetNumberOfPoints()
<< " points and " << result.GetNumberOfCells() << " triangles\n";
}
catch (const vtkm::cont::ErrorUserAbort& e)
{
VTKM_TEST_FAIL("Execution was unexpectedly aborted");
}
return 0;
}
} // anon namespace
int UnitTestAbort(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(TestAbort, argc, argv);
}

@ -9,6 +9,7 @@ DEPENDS
OPTIONAL_DEPENDS
vtkm_loguru
TEST_OPTIONAL_DEPENDS
vtkm_filter_contour
vtkm_filter_field_conversion
TEST_DEPENDS
vtkm_source