Commit fbc43463 authored by Kenneth Moreland's avatar Kenneth Moreland
Browse files

Fix threaded device allocation

We are starting to introduce threads in the VTK-m control environment so
that we can schedule multiple things at once. Once issue we ran into is
that some methods to allocate data on devices are not thread safe. To
fix this, we make allocating memory for a `Buffer` thread safe. This
could potentially slow things down waiting for mutexes, but you are
hopefully not spending a lot of time allocating/deallocating memory
anyway.
parent 25ed066a
Pipeline #239798 failed with stage
......@@ -65,6 +65,7 @@ set(unit_tests
UnitTestStreamSurfaceFilter.cxx
UnitTestSurfaceNormalsFilter.cxx
UnitTestTetrahedralizeFilter.cxx
UnitTestThreadedFilterExecute.cxx
UnitTestThresholdFilter.cxx
UnitTestThresholdPointsFilter.cxx
UnitTestTriangulateFilter.cxx
......
//============================================================================
// 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/ArrayCopy.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/source/Tangle.h>
#include <vtkm/filter/CleanGrid.h>
#include <vtkm/filter/Contour.h>
#include <vtkm/filter/ClipWithField.h>
#include <vtkm/cont/testing/Testing.h>
#include <array>
#include <future>
namespace
{
constexpr std::size_t NUM_THREADS = 64;
using WorkType = vtkm::cont::DataSet(std::size_t);
vtkm::cont::DataSet MakeTangle(std::size_t index)
{
vtkm::source::Tangle tangle(vtkm::Id3(index + 1));
vtkm::cont::DataSet data = tangle.Execute();
// Kick data off the device (so it has to be moved back on).
data.GetField("nodevar").GetData().ReleaseResourcesExecution();
return data;
}
vtkm::cont::DataSet DoClipWithField(std::size_t index)
{
vtkm::cont::DataSet inputData = MakeTangle(index);
vtkm::filter::ClipWithField clip;
clip.SetClipValue(0.0);
clip.SetActiveField("nodevar");
clip.SetFieldsToPass("nodevar", vtkm::cont::Field::Association::POINTS);
return clip.Execute(inputData);
}
vtkm::cont::DataSet DoContour(std::size_t index)
{
vtkm::cont::DataSet inputData = MakeTangle(index);
vtkm::filter::Contour contour;
contour.SetGenerateNormals(true);
contour.SetIsoValue(0, 0.5);
contour.SetActiveField("nodevar");
contour.SetFieldsToPass("nodevar", vtkm::cont::Field::Association::POINTS);
return contour.Execute(inputData);
}
vtkm::cont::DataSet DoCleanGrid(std::size_t index)
{
vtkm::cont::DataSet inputData = MakeTangle(index);
vtkm::filter::CleanGrid clean;
clean.SetCompactPointFields(true);
clean.SetMergePoints(true);
return clean.Execute(inputData);
}
vtkm::cont::DataSet WrapWorkFunction(WorkType f, std::size_t index, const vtkm::cont::RuntimeDeviceTracker& tracker)
{
// Each thread has its own RuntimeDeviceTracker (to allow you to control different devices
// on different threads). But that means that each thread creates its own tracker. We
// want all the threads to respect the runtime set up on the main thread, so copy the state
// of that tracker (passed as an argument) to this thread.
vtkm::cont::GetRuntimeDeviceTracker().CopyState(tracker);
return f(index);
}
void CompareThreadedSequential(WorkType f)
{
std::array<std::future<vtkm::cont::DataSet>, NUM_THREADS> futures;
vtkm::cont::RuntimeDeviceTracker& tracker = vtkm::cont::GetRuntimeDeviceTracker();
// Run lots of execute operations at the same time.
for (std::size_t threadIndex = 0; threadIndex < NUM_THREADS; ++threadIndex)
{
futures[threadIndex] = std::async(
std::launch::async, [=, &tracker](){return WrapWorkFunction(f, threadIndex, tracker);});
}
// Wait for each operation to complete and check result.
for (std::size_t threadIndex = 0; threadIndex < NUM_THREADS; ++threadIndex)
{
vtkm::cont::DataSet localCompute = f(threadIndex);
VTKM_TEST_ASSERT(
test_equal_ArrayHandles(localCompute.GetField("nodevar").GetData(),
futures[threadIndex].get().GetField("nodevar").GetData()));
}
}
void DoTest()
{
std::cout << "ClipWithField" << std::endl;
CompareThreadedSequential(DoClipWithField);
std::cout << "Contour" << std::endl;
CompareThreadedSequential(DoContour);
std::cout << "CleanGrid" << std::endl;
CompareThreadedSequential(DoCleanGrid);
}
} // anonymous namespace
int UnitTestThreadedFilterExecute(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(DoTest, argc, argv);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment