From d093af6cd71a54a885f49484b2c8e6b4a13ad258 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Mon, 23 Jun 2025 09:09:33 -0400 Subject: [PATCH 01/17] Updated vcpkg with spdlog Signed-off-by: Jared Duffey --- vcpkg-configuration.json | 1 + vcpkg.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json index fbb4cd9037..0997c8b3a8 100644 --- a/vcpkg-configuration.json +++ b/vcpkg-configuration.json @@ -27,6 +27,7 @@ "reproc", "snappy", "span-lite", + "spdlog", "tbb", "vcpkg-cmake", "vcpkg-cmake-config", diff --git a/vcpkg.json b/vcpkg.json index 9742ed1108..421aaffc70 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -33,6 +33,9 @@ }, { "name": "reproc" + }, + { + "name": "spdlog" } ], "features": { From ecedc60d93db4bf015dc9625f2e4a9185e03d77c Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Mon, 23 Jun 2025 09:10:10 -0400 Subject: [PATCH 02/17] Added spdlog to cmake Signed-off-by: Jared Duffey --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 526f961cca..4828af59e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,6 +188,7 @@ find_package(span-lite CONFIG REQUIRED) find_package(Eigen3 REQUIRED) find_package(boost_mp11 CONFIG REQUIRED) find_package(nod CONFIG REQUIRED) +find_package(spdlog CONFIG REQUIRED) # ----------------------------------------------------------------------- # Find HDF5 and get the path to the DLL libraries and put that into a @@ -265,6 +266,7 @@ target_link_libraries(simplnx HDF5::HDF5 Boost::mp11 nod::nod + spdlog::spdlog ) if(UNIX) From c4235f72fb452ac4d06de7f9a837b18f128f6282 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Mon, 23 Jun 2025 09:33:15 -0400 Subject: [PATCH 03/17] Changed IFilter::MessageHandler to move string Signed-off-by: Jared Duffey --- src/simplnx/Filter/IFilter.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/simplnx/Filter/IFilter.hpp b/src/simplnx/Filter/IFilter.hpp index 74f0001106..086bdcb4b6 100644 --- a/src/simplnx/Filter/IFilter.hpp +++ b/src/simplnx/Filter/IFilter.hpp @@ -62,17 +62,17 @@ class SIMPLNX_EXPORT IFilter m_Callback(message); } } - void operator()(const std::string& message) const + void operator()(std::string message) const { - operator()(Message{Message::Type::Info, message}); + operator()(Message{Message::Type::Info, std::move(message)}); } - void operator()(Message::Type type, const std::string& message) const + void operator()(Message::Type type, std::string message) const { - operator()(Message{type, message}); + operator()(Message{type, std::move(message)}); } - void operator()(Message::Type type, const std::string& message, int32 progress) const + void operator()(Message::Type type, std::string message, int32 progress) const { - operator()(ProgressMessage{type, message, progress}); + operator()(ProgressMessage{type, std::move(message), progress}); } Callback m_Callback; }; From d801ede6f46063022f67c31aae84088aea785d57 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Mon, 23 Jun 2025 09:28:21 -0400 Subject: [PATCH 04/17] Added MessageHelper Signed-off-by: Jared Duffey --- CMakeLists.txt | 2 + src/simplnx/Utilities/MessageHelper.cpp | 114 +++++++++++++ src/simplnx/Utilities/MessageHelper.hpp | 218 ++++++++++++++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 src/simplnx/Utilities/MessageHelper.cpp create mode 100644 src/simplnx/Utilities/MessageHelper.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4828af59e7..4ad99f6237 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -544,6 +544,7 @@ set(SIMPLNX_HDRS ${SIMPLNX_SOURCE_DIR}/Utilities/HistogramUtilities.hpp ${SIMPLNX_SOURCE_DIR}/Utilities/MaskCompareUtilities.hpp ${SIMPLNX_SOURCE_DIR}/Utilities/MemoryUtilities.hpp + ${SIMPLNX_SOURCE_DIR}/Utilities/MessageHelper.hpp ${SIMPLNX_SOURCE_DIR}/Utilities/StringUtilities.hpp ${SIMPLNX_SOURCE_DIR}/Utilities/StringInterpretationUtilities.hpp ${SIMPLNX_SOURCE_DIR}/Utilities/IntersectionUtilities.hpp @@ -747,6 +748,7 @@ set(SIMPLNX_SRCS ${SIMPLNX_SOURCE_DIR}/Utilities/DataStoreUtilities.cpp ${SIMPLNX_SOURCE_DIR}/Utilities/MaskCompareUtilities.cpp ${SIMPLNX_SOURCE_DIR}/Utilities/MemoryUtilities.cpp + ${SIMPLNX_SOURCE_DIR}/Utilities/MessageHelper.cpp ${SIMPLNX_SOURCE_DIR}/Utilities/IParallelAlgorithm.cpp ${SIMPLNX_SOURCE_DIR}/Utilities/ParallelDataAlgorithm.cpp ${SIMPLNX_SOURCE_DIR}/Utilities/ParallelData2DAlgorithm.cpp diff --git a/src/simplnx/Utilities/MessageHelper.cpp b/src/simplnx/Utilities/MessageHelper.cpp new file mode 100644 index 0000000000..9362d3953a --- /dev/null +++ b/src/simplnx/Utilities/MessageHelper.cpp @@ -0,0 +1,114 @@ +#include "MessageHelper.hpp" + +#include + +#include +#include +#include +#include +#include + +#include + +namespace nx::core +{ +namespace +{ +template +class MessageHandlerSink : public spdlog::sinks::base_sink +{ +public: + MessageHandlerSink(const IFilter::MessageHandler& messageHandler) + : spdlog::sinks::base_sink() + , m_MessageHandler(messageHandler) + { + } + + ~MessageHandlerSink() noexcept = default; + + MessageHandlerSink(const MessageHandlerSink&) = delete; + MessageHandlerSink(MessageHandlerSink&&) = delete; + + MessageHandlerSink& operator=(const MessageHandlerSink&) = delete; + MessageHandlerSink& operator=(MessageHandlerSink&&) = delete; + +protected: + void sink_it_(const spdlog::details::log_msg& msg) override + { + m_MessageHandler({IFilter::ProgressMessage::Type::Info, fmt::to_string(msg.payload)}); + } + + void flush_() override + { + } + +private: + const IFilter::MessageHandler& m_MessageHandler; +}; + +using MessageHandlerSink_mt = MessageHandlerSink; +using MessageHandlerSink_st = MessageHandlerSink; +} // namespace + +struct Messenger::Impl +{ + const IFilter::MessageHandler& m_MessageHandler; + + std::shared_ptr m_ThrottledLogger = nullptr; + std::shared_ptr m_MandatoryLogger = nullptr; + + Impl() = delete; + + Impl(const IFilter::MessageHandler& messageHandler) + : m_MessageHandler(messageHandler) + { + auto sink = std::make_shared(m_MessageHandler); + spdlog::init_thread_pool(spdlog::details::default_async_q_size, 1U); + auto threadPool = spdlog::thread_pool(); + m_MandatoryLogger = std::make_shared("MessageHandlerMandatoryLogger", sink, threadPool, spdlog::async_overflow_policy::block); + m_ThrottledLogger = std::make_shared("MessageHandlerThrottledLogger", sink, threadPool, spdlog::async_overflow_policy::overrun_oldest); + } + + ~Impl() noexcept + { + spdlog::shutdown(); + } + + Impl(const Impl&) = delete; + Impl(Impl&&) noexcept = delete; + + Impl& operator=(const Impl&) = delete; + Impl& operator=(Impl&&) noexcept = delete; + + void sendMessage(std::string message) + { + m_MandatoryLogger->info(message); + } + + void trySendMessage(std::string message) + { + m_ThrottledLogger->info(message); + } +}; + +Messenger::Messenger(const IFilter::MessageHandler& messageHandler) +: m_Impl(std::make_unique(messageHandler)) +{ +} + +Messenger::~Messenger() noexcept = default; + +Messenger::Messenger(Messenger&&) noexcept = default; + +Messenger& Messenger::operator=(Messenger&&) noexcept = default; + +void Messenger::sendMessage(std::string message) +{ + m_Impl->sendMessage(std::move(message)); +} + +void Messenger::trySendMessage(std::string message) +{ + m_Impl->trySendMessage(std::move(message)); +} +} // namespace nx::core diff --git a/src/simplnx/Utilities/MessageHelper.hpp b/src/simplnx/Utilities/MessageHelper.hpp new file mode 100644 index 0000000000..e02fa7dbd7 --- /dev/null +++ b/src/simplnx/Utilities/MessageHelper.hpp @@ -0,0 +1,218 @@ +#pragma once + +#include "simplnx/Filter/IFilter.hpp" +#include "simplnx/simplnx_export.hpp" + +#include +#include +#include +#include +#include + +namespace nx::core +{ +inline constexpr int32 CalculatePercentCompleteAsInt(usize currentProgress, usize max) +{ + return static_cast(static_cast(currentProgress) / static_cast(max) * 100.0f); +} + +class SIMPLNX_EXPORT Messenger +{ +public: + Messenger() = delete; + + Messenger(const IFilter::MessageHandler& messageHandler); + + ~Messenger() noexcept; + + Messenger(const Messenger&) = delete; + Messenger(Messenger&&) noexcept; + + Messenger& operator=(const Messenger&) = delete; + Messenger& operator=(Messenger&&) noexcept; + + void sendMessage(std::string message); + void trySendMessage(std::string message); + +private: + struct Impl; + std::unique_ptr m_Impl = nullptr; +}; + +template +concept ThrottledMessageFunctor = std::is_invocable_r_v; + +class ThrottledMessenger +{ +public: + ThrottledMessenger() = delete; + + ThrottledMessenger(std::shared_ptr messenger, std::chrono::milliseconds interval) + : m_Messenger(std::move(messenger)) + , m_LastTime(std::chrono::steady_clock::now()) + , m_Interval(interval) + { + } + + void sendThrottledMessage(ThrottledMessageFunctor auto functor) + { + auto now = std::chrono::steady_clock::now(); + m_LastTimeDiff = now - m_LastTime; + if(m_LastTimeDiff >= m_Interval) + { + m_LastTime = now; + m_Messenger->trySendMessage(functor()); + } + } + + std::chrono::steady_clock::time_point getLastTime() const + { + return m_LastTime; + } + + std::chrono::steady_clock::duration getLastTimeDiff() const + { + return m_LastTimeDiff; + } + +private: + std::shared_ptr m_Messenger = nullptr; + std::chrono::steady_clock::time_point m_LastTime = {}; + std::chrono::milliseconds m_Interval = {}; + std::chrono::steady_clock::duration m_LastTimeDiff = {}; +}; + +template +concept ProgressMessageFunctor = std::is_invocable_r_v; + +struct ProgressMessageData +{ + usize m_MaxProgress = 0; + std::atomic_size_t m_CurrentProgress = 0; + std::string m_MessageTemplate; +}; + +class ProgressMessenger +{ +public: + ProgressMessenger() = delete; + + ProgressMessenger(std::shared_ptr progressMessageData, std::shared_ptr messenger, std::chrono::milliseconds interval) + : m_ThrottledMessenger(std::move(messenger), interval) + , m_ProgressMessageData(std::move(progressMessageData)) + { + } + + void sendThrottledMessage(ThrottledMessageFunctor auto functor) + { + m_ThrottledMessenger.sendThrottledMessage(functor); + } + + void sendProgressMessage(usize increment, ProgressMessageFunctor auto functor) + { + m_ProgressMessageData->m_CurrentProgress += increment; + auto nestedFunctor = [this, functor]() { return functor(m_ProgressMessageData->m_CurrentProgress.load(), m_ProgressMessageData->m_MaxProgress); }; + sendThrottledMessage(nestedFunctor); + } + + void sendProgressMessage(usize increment) + { + auto func = [this](usize currentProgress, usize maxProgress) { + int32 percentComplete = CalculatePercentCompleteAsInt(currentProgress, maxProgress); + return fmt::format(fmt::runtime(m_ProgressMessageData->m_MessageTemplate), percentComplete); + }; + + sendProgressMessage(increment, func); + } + + const ProgressMessageData& getProgressMessageData() const + { + return *m_ProgressMessageData; + } + + const ThrottledMessenger& getThrottledMessenger() const + { + return m_ThrottledMessenger; + } + +private: + ThrottledMessenger m_ThrottledMessenger; + std::shared_ptr m_ProgressMessageData = nullptr; +}; + +class ProgressMessageHelper +{ +public: + ProgressMessageHelper(std::shared_ptr messenger) + : m_Messenger(std::move(messenger)) + , m_ProgressMessageData(std::make_shared()) + { + } + + ProgressMessenger createProgressMessenger(std::chrono::milliseconds interval = std::chrono::milliseconds(1000)) + { + return ProgressMessenger(m_ProgressMessageData, m_Messenger, interval); + } + + void setProgressMessageTemplate(std::string messageTemplate) + { + m_ProgressMessageData->m_MessageTemplate = std::move(messageTemplate); + } + + void setMaxProgresss(usize maxProgress) + { + m_ProgressMessageData->m_MaxProgress = maxProgress; + } + + void resetProgress() + { + m_ProgressMessageData->m_CurrentProgress = 0; + } + +private: + std::shared_ptr m_Messenger = nullptr; + std::shared_ptr m_ProgressMessageData = nullptr; +}; + +class MessageHelper +{ +public: + MessageHelper() = delete; + + MessageHelper(const IFilter::MessageHandler& messageHandler) + : m_Messenger(std::make_shared(messageHandler)) + { + } + + ~MessageHelper() noexcept = default; + + MessageHelper(const MessageHelper&) = delete; + MessageHelper(MessageHelper&&) noexcept = default; + + MessageHelper& operator=(const MessageHelper&) = delete; + MessageHelper& operator=(MessageHelper&&) noexcept = default; + + void sendMessage(std::string message) + { + m_Messenger->sendMessage(std::move(message)); + } + + void trySendMessage(std::string message) + { + m_Messenger->trySendMessage(std::move(message)); + } + + ThrottledMessenger createThrottledMessenger(std::chrono::milliseconds interval = std::chrono::milliseconds(1000)) + { + return ThrottledMessenger(m_Messenger, interval); + } + + ProgressMessageHelper createProgressMessageHelper() + { + return ProgressMessageHelper(m_Messenger); + } + +private: + std::shared_ptr m_Messenger = nullptr; +}; +} // namespace nx::core From 709afd523278caa862de4dca443ae90a3276d199 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Mon, 23 Jun 2025 16:42:18 -0400 Subject: [PATCH 05/17] Updated filters Signed-off-by: Jared Duffey --- .../AlignSectionsMisorientation.cpp | 24 +-- .../BadDataNeighborOrientationCheck.cpp | 29 +--- .../Filters/Algorithms/ComputeGBCD.cpp | 25 +-- .../ComputeKernelAvgMisorientations.cpp | 59 +++---- .../ComputeKernelAvgMisorientations.hpp | 11 -- .../NeighborOrientationCorrelation.cpp | 54 ++----- .../NeighborOrientationCorrelation.hpp | 3 - .../AlignSectionsFeatureCentroid.cpp | 12 +- .../CalculateTriangleGroupCurvatures.cpp | 8 +- .../CalculateTriangleGroupCurvatures.hpp | 5 +- .../ComputeArrayHistogramByFeature.cpp | 87 +++------- .../ComputeArrayHistogramByFeature.hpp | 10 -- .../Algorithms/ComputeArrayStatistics.cpp | 149 +++++++----------- .../Algorithms/ComputeArrayStatistics.hpp | 13 -- .../ComputeNeighborListStatistics.cpp | 40 ++--- .../ComputeNeighborListStatistics.hpp | 8 - .../Algorithms/ComputeNeighborhoods.cpp | 46 ++---- .../Algorithms/ComputeNeighborhoods.hpp | 8 +- .../ComputeVertexToTriangleDistances.cpp | 40 ++--- .../ComputeVertexToTriangleDistances.hpp | 8 - .../SimplnxCore/Filters/Algorithms/DBSCAN.cpp | 52 +++--- .../SimplnxCore/Filters/Algorithms/DBSCAN.hpp | 1 - .../Filters/Algorithms/ErodeDilateBadData.cpp | 29 ++-- .../Filters/Algorithms/ErodeDilateBadData.hpp | 1 - .../Algorithms/FeatureFaceCurvature.cpp | 38 ++--- .../Algorithms/FeatureFaceCurvature.hpp | 9 -- .../Filters/Algorithms/ReadStlFile.cpp | 17 +- .../RegularGridSampleSurfaceMesh.cpp | 1 - .../Algorithms/RemoveFlaggedFeatures.cpp | 30 +--- .../Algorithms/RemoveFlaggedFeatures.hpp | 8 - .../Filters/ComputeFeatureNeighborsFilter.cpp | 39 +---- .../Filters/IterativeClosestPointFilter.cpp | 11 +- .../MapPointCloudToRegularGridFilter.cpp | 27 ++-- src/simplnx/Utilities/AlignSections.cpp | 22 ++- src/simplnx/Utilities/AlignSections.hpp | 4 +- src/simplnx/Utilities/SampleSurfaceMesh.cpp | 60 +++---- src/simplnx/Utilities/SampleSurfaceMesh.hpp | 11 +- 37 files changed, 314 insertions(+), 685 deletions(-) diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMisorientation.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMisorientation.cpp index ca92431126..cf9d7a72d7 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMisorientation.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMisorientation.cpp @@ -72,8 +72,6 @@ Result<> AlignSectionsMisorientation::findShifts(std::vector& xShifts, std::vector orientationOps = LaueOps::GetAllOrientationOps(); - int32_t progInt = 0; - // Allocate a 2D Array which will be reused from slice to slice std::vector misorients(dims[0] * dims[1], false); @@ -81,7 +79,7 @@ Result<> AlignSectionsMisorientation::findShifts(std::vector& xShifts, const auto halfDim1 = static_cast(dims[1] * 0.5f); double deg2Rad = (nx::core::numbers::pi / 180.0); - auto start = std::chrono::steady_clock::now(); + ThrottledMessenger throttledMessenger = getMessageHelper().createThrottledMessenger(); if(m_InputValues->StoreAlignmentShifts) { auto& slicesStore = m_DataStructure.getDataAs(m_InputValues->SlicesArrayPath)->getDataStoreRef(); @@ -94,15 +92,7 @@ Result<> AlignSectionsMisorientation::findShifts(std::vector& xShifts, { return {}; } - progInt = static_cast(iter) / static_cast(dims[2]) * 100.0f; - auto now = std::chrono::steady_clock::now(); - // Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - std::string message = fmt::format("Determining Shifts || {}% Complete", progInt); - m_MessageHandler(nx::core::IFilter::ProgressMessage{nx::core::IFilter::Message::Type::Info, message, progInt}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Shifts || {}% Complete", CalculatePercentCompleteAsInt(iter, dims[2])); }); if(getCancel()) { return {}; @@ -210,15 +200,7 @@ Result<> AlignSectionsMisorientation::findShifts(std::vector& xShifts, // Loop over the Z Direction for(int64_t iter = 1; iter < dims[2]; iter++) { - progInt = static_cast(iter) / static_cast(dims[2]) * 100.0f; - auto now = std::chrono::steady_clock::now(); - // Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - std::string message = fmt::format("Determining Shifts || {}% Complete", progInt); - m_MessageHandler(nx::core::IFilter::ProgressMessage{nx::core::IFilter::Message::Type::Info, message, progInt}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Shifts || {}% Complete", CalculatePercentCompleteAsInt(iter, dims[2])); }); if(getCancel()) { return {}; diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheck.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheck.cpp index 9d0da8411a..8483957cb9 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheck.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheck.cpp @@ -5,6 +5,7 @@ #include "simplnx/DataStructure/DataGroup.hpp" #include "simplnx/DataStructure/Geometry/ImageGeom.hpp" #include "simplnx/Utilities/MaskCompareUtilities.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "EbsdLib/Core/Orientation.hpp" #include "EbsdLib/LaueOps/LaueOps.h" @@ -80,18 +81,11 @@ Result<> BadDataNeighborOrientationCheck::operator()() std::vector neighborCount(totalPoints, 0); - int64_t progressInt = 0; - auto start = std::chrono::steady_clock::now(); + MessageHelper messageHelper(m_MessageHandler); + ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); for(size_t i = 0; i < totalPoints; i++) { - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - start).count() > 1000) - { - progressInt = static_cast((static_cast(i) / totalPoints) * 100.0f); - std::string ss = fmt::format("Processing Data '{}'% completed", progressInt); - m_MessageHandler({IFilter::Message::Type::Info, ss}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing Data '{}'% completed", CalculatePercentCompleteAsInt(i, totalPoints)); }); if(!maskCompare->isTrue(i)) { @@ -157,19 +151,12 @@ Result<> BadDataNeighborOrientationCheck::operator()() while(counter > 0) { counter = 0; - progressInt = 0; - start = std::chrono::steady_clock::now(); for(size_t i = 0; i < totalPoints; i++) { - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - start).count() > 1000) - { - progressInt = static_cast((static_cast(i) / totalPoints) * 100.0f); - std::string ss = - fmt::format("Level '{}' of '{}' || Processing Data ('{}') '{}'% completed", (startLevel - currentLevel) + 1, startLevel - m_InputValues->NumberOfNeighbors, loopNumber, progressInt); - m_MessageHandler({IFilter::Message::Type::Info, ss}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { + return fmt::format("Level '{}' of '{}' || Processing Data ('{}') '{}'% completed", (startLevel - currentLevel) + 1, startLevel - m_InputValues->NumberOfNeighbors, loopNumber, + CalculatePercentCompleteAsInt(i, totalPoints)); + }); if(neighborCount[i] >= currentLevel && !maskCompare->isTrue(i)) { diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeGBCD.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeGBCD.cpp index 4684391f3b..7efbf4e516 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeGBCD.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeGBCD.cpp @@ -4,6 +4,7 @@ #include "simplnx/DataStructure/DataArray.hpp" #include "simplnx/DataStructure/DataGroup.hpp" #include "simplnx/Utilities/Math/MatrixMath.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelDataAlgorithm.hpp" #include "simplnx/Utilities/TimeUtilities.hpp" @@ -389,11 +390,13 @@ Result<> ComputeGBCD::operator()() SizeGBCD sizeGbcd(triangleChunkSize, k_NumMisoReps, m_InputValues->GBCDRes); int32 totalGBCDBins = sizeGbcd.m_GbcdSizes[0] * sizeGbcd.m_GbcdSizes[1] * sizeGbcd.m_GbcdSizes[2] * sizeGbcd.m_GbcdSizes[3] * sizeGbcd.m_GbcdSizes[4] * 2; + MessageHelper messageHelper(m_MessageHandler); + // create an array to hold the total face area for each phase and initialize the array to 0.0 std::vector totalFaceArea(totalPhases, 0.0); - std::string ss = fmt::format("1/2 Starting GBCD Calculation and Summation Phase"); - m_MessageHandler({IFilter::Message::Type::Info, ss}); - auto startMillis = std::chrono::steady_clock::now(); + auto startTime = std::chrono::steady_clock::now(); + messageHelper.sendMessage("1/2 Starting GBCD Calculation and Summation Phase"); + ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); for(usize i = 0; i < totalFaces; i = i + triangleChunkSize) { @@ -448,19 +451,17 @@ Result<> ComputeGBCD::operator()() } } - auto currentMillis = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(currentMillis - startMillis).count() > 1000) - { + throttledMessenger.sendThrottledMessage([&]() { + auto currentTime = throttledMessenger.getLastTime(); const usize k_LastTriangleIndex = i + triangleChunkSize; - float32 currentRate = static_cast(triangleChunkSize) / static_cast(std::chrono::duration_cast(currentMillis - startMillis).count()); + float32 currentRate = static_cast(triangleChunkSize) / static_cast(std::chrono::duration_cast(currentTime - startTime).count()); uint64 estimatedTime = static_cast(totalFaces - k_LastTriangleIndex) / currentRate; - ss = fmt::format("Calculating GBCD || Triangles {}/{} Completed || Est. Time Remain: {}", k_LastTriangleIndex, totalFaces, ConvertMillisToHrsMinSecs(estimatedTime)); - startMillis = std::chrono::steady_clock::now(); - m_MessageHandler({IFilter::Message::Type::Info, ss}); - } + startTime = currentTime; + return fmt::format("Calculating GBCD || Triangles {}/{} Completed || Est. Time Remain: {}", k_LastTriangleIndex, totalFaces, ConvertMillisToHrsMinSecs(estimatedTime)); + }); } - m_MessageHandler({IFilter::Message::Type::Info, "2/2 Starting GBCD Normalization Phase"}); + messageHelper.sendMessage("2/2 Starting GBCD Normalization Phase"); for(int32 i = 0; i < totalPhases; i++) { diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.cpp index e345f89d9e..9c3d47826b 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.cpp @@ -4,6 +4,7 @@ #include "simplnx/DataStructure/DataArray.hpp" #include "simplnx/DataStructure/DataGroup.hpp" #include "simplnx/DataStructure/Geometry/ImageGeom.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelData3DAlgorithm.hpp" #include "EbsdLib/LaueOps/LaueOps.h" @@ -17,9 +18,9 @@ namespace class FindKernelAvgMisorientationsImpl { public: - FindKernelAvgMisorientationsImpl(ComputeKernelAvgMisorientations* filter, DataStructure& dataStructure, const ComputeKernelAvgMisorientationsInputValues* inputValues, + FindKernelAvgMisorientationsImpl(ProgressMessageHelper& progressMessenger, DataStructure& dataStructure, const ComputeKernelAvgMisorientationsInputValues* inputValues, const std::atomic_bool& shouldCancel) - : m_Filter(filter) + : m_ProgressMessageHelper(progressMessenger) , m_DataStructure(dataStructure) , m_InputValues(inputValues) , m_ShouldCancel(shouldCancel) @@ -54,7 +55,8 @@ class FindKernelAvgMisorientationsImpl // messenger values usize counter = 0; usize increment = (zEnd - zStart) / 100; - auto start = std::chrono::steady_clock::now(); + + ProgressMessenger progressMessenger = m_ProgressMessageHelper.createProgressMessenger(); auto xPoints = static_cast(udims[0]); auto yPoints = static_cast(udims[1]); @@ -68,13 +70,11 @@ class FindKernelAvgMisorientationsImpl if(counter > increment) { - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - start).count() > 1000) - { - m_Filter->sendThreadSafeProgressMessage(counter); - counter = 0; - start = std::chrono::steady_clock::now(); - } + progressMessenger.sendProgressMessage(counter, [&](usize currentProgress, usize maxProgress) { + int32 percentComplete = CalculatePercentCompleteAsInt(currentProgress, maxProgress); + return fmt::format(fmt::runtime(progressMessenger.getProgressMessageData().m_MessageTemplate), percentComplete); + }); + counter = 0; } for(size_t row = yStart; row < yEnd; row++) @@ -145,7 +145,7 @@ class FindKernelAvgMisorientationsImpl } } } - m_Filter->sendThreadSafeProgressMessage(counter); + progressMessenger.sendProgressMessage(counter); } void operator()(const Range3D& range) const @@ -154,7 +154,7 @@ class FindKernelAvgMisorientationsImpl } private: - ComputeKernelAvgMisorientations* m_Filter = nullptr; + ProgressMessageHelper& m_ProgressMessageHelper; DataStructure& m_DataStructure; const ComputeKernelAvgMisorientationsInputValues* m_InputValues = nullptr; const std::atomic_bool& m_ShouldCancel; @@ -175,40 +175,17 @@ ComputeKernelAvgMisorientations::ComputeKernelAvgMisorientations(DataStructure& // ----------------------------------------------------------------------------- ComputeKernelAvgMisorientations::~ComputeKernelAvgMisorientations() noexcept = default; -// ----------------------------------------------------------------------------- -const std::atomic_bool& ComputeKernelAvgMisorientations::getCancel() -{ - return m_ShouldCancel; -} - -// ----------------------------------------------------------------------------- -void ComputeKernelAvgMisorientations::sendThreadSafeProgressMessage(usize counter) -{ - std::lock_guard guard(m_ProgressMessage_Mutex); - - m_ProgressCounter += counter; - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - m_InitialPoint).count() < 1000) - { - return; - } - - auto progressInt = static_cast((static_cast(m_ProgressCounter) / static_cast(m_TotalElements)) * 100.0f); - std::string ss = fmt::format("Finding Kernel Average Misorientations || {}%", progressInt); - m_MessageHandler(IFilter::Message::Type::Info, ss); - - m_LastProgressInt = progressInt; - m_InitialPoint = std::chrono::steady_clock::now(); -} - // ----------------------------------------------------------------------------- Result<> ComputeKernelAvgMisorientations::operator()() { auto* gridGeom = m_DataStructure.getDataAs(m_InputValues->InputImageGeometry); SizeVec3 udims = gridGeom->getDimensions(); - // set up threadsafe messenger - m_TotalElements = udims[2] * udims[1] * udims[0]; + MessageHelper messageHelper(m_MessageHandler); + ProgressMessageHelper progressMessageHelper = messageHelper.createProgressMessageHelper(); + + progressMessageHelper.setMaxProgresss(udims[2] * udims[1] * udims[0]); + progressMessageHelper.setProgressMessageTemplate("Finding Kernel Average Misorientations || {}%"); typename IParallelAlgorithm::AlgorithmArrays algArrays; algArrays.push_back(m_DataStructure.getDataAs(m_InputValues->CellPhasesArrayPath)); @@ -220,7 +197,7 @@ Result<> ComputeKernelAvgMisorientations::operator()() ParallelData3DAlgorithm parallelAlgorithm; parallelAlgorithm.setRange(Range3D(0, udims[0], 0, udims[1], 0, udims[2])); parallelAlgorithm.requireArraysInMemory(algArrays); - parallelAlgorithm.execute(FindKernelAvgMisorientationsImpl(this, m_DataStructure, m_InputValues, m_ShouldCancel)); + parallelAlgorithm.execute(FindKernelAvgMisorientationsImpl(progressMessageHelper, m_DataStructure, m_InputValues, m_ShouldCancel)); return {}; } diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.hpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.hpp index 408d5020fc..4fe32ce72d 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.hpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.hpp @@ -40,22 +40,11 @@ class ORIENTATIONANALYSIS_EXPORT ComputeKernelAvgMisorientations Result<> operator()(); - const std::atomic_bool& getCancel(); - - void sendThreadSafeProgressMessage(usize counter); - private: DataStructure& m_DataStructure; const ComputeKernelAvgMisorientationsInputValues* m_InputValues = nullptr; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; - - // Thread safe Progress Message - std::chrono::steady_clock::time_point m_InitialPoint = std::chrono::steady_clock::now(); - mutable std::mutex m_ProgressMessage_Mutex; - size_t m_TotalElements = 0; - size_t m_ProgressCounter = 0; - size_t m_LastProgressInt = 0; }; } // namespace nx::core diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.cpp index 0d172200fd..a41ddafb85 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.cpp @@ -5,6 +5,7 @@ #include "simplnx/DataStructure/DataGroup.hpp" #include "simplnx/DataStructure/Geometry/ImageGeom.hpp" #include "simplnx/Utilities/DataGroupUtilities.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelTaskAlgorithm.hpp" #ifdef SIMPLNX_ENABLE_MULTICORE @@ -23,8 +24,8 @@ class NeighborOrientationCorrelationTransferDataImpl NeighborOrientationCorrelationTransferDataImpl() = delete; NeighborOrientationCorrelationTransferDataImpl(const NeighborOrientationCorrelationTransferDataImpl&) = default; - NeighborOrientationCorrelationTransferDataImpl(NeighborOrientationCorrelation* filterAlg, size_t totalPoints, const std::vector& bestNeighbor, std::shared_ptr dataArrayPtr) - : m_FilterAlg(filterAlg) + NeighborOrientationCorrelationTransferDataImpl(MessageHelper& messageHelper, size_t totalPoints, const std::vector& bestNeighbor, std::shared_ptr dataArrayPtr) + : m_MessageHelper(messageHelper) , m_TotalPoints(totalPoints) , m_BestNeighbor(bestNeighbor) , m_DataArrayPtr(dataArrayPtr) @@ -38,17 +39,11 @@ class NeighborOrientationCorrelationTransferDataImpl void operator()() const { - auto start = std::chrono::steady_clock::now(); + ThrottledMessenger throttledMessenger = m_MessageHelper.createThrottledMessenger(); + std::string arrayName = m_DataArrayPtr->getName(); for(size_t i = 0; i < m_TotalPoints; i++) { - auto now = std::chrono::steady_clock::now(); - //// Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - float progress = static_cast(i) / static_cast(m_TotalPoints) * 100.0f; - m_FilterAlg->updateProgress(fmt::format("Processing {}: {}% completed", m_DataArrayPtr->getName(), static_cast(progress))); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing {}: {}% completed", arrayName, CalculatePercentCompleteAsInt(i, m_TotalPoints)); }); int64_t neighbor = m_BestNeighbor[i]; if(neighbor != -1) { @@ -58,7 +53,7 @@ class NeighborOrientationCorrelationTransferDataImpl } private: - NeighborOrientationCorrelation* m_FilterAlg = nullptr; + MessageHelper& m_MessageHelper; size_t m_TotalPoints = 0; std::vector m_BestNeighbor; std::shared_ptr m_DataArrayPtr; @@ -77,18 +72,6 @@ NeighborOrientationCorrelation::NeighborOrientationCorrelation(DataStructure& da // ----------------------------------------------------------------------------- NeighborOrientationCorrelation::~NeighborOrientationCorrelation() noexcept = default; -// ----------------------------------------------------------------------------- -const std::atomic_bool& NeighborOrientationCorrelation::getCancel() -{ - return m_ShouldCancel; -} - -// ----------------------------------------------------------------------------- -void NeighborOrientationCorrelation::updateProgress(const std::string& progMessage) -{ - m_MessageHandler({IFilter::Message::Type::Info, progMessage}); -} - // ----------------------------------------------------------------------------- Result<> NeighborOrientationCorrelation::operator()() { @@ -137,25 +120,22 @@ Result<> NeighborOrientationCorrelation::operator()() const int32_t startLevel = 6; float* currentQuatPtr = nullptr; + MessageHelper messageHelper(m_MessageHandler); + + ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); + for(int32_t currentLevel = startLevel; currentLevel > m_InputValues->Level; currentLevel--) { - if(getCancel()) + if(m_ShouldCancel) { break; } - int64_t progressInt = 0; - auto start = std::chrono::steady_clock::now(); for(size_t i = 0; i < totalPoints; i++) { - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - start).count() > 1000) - { - progressInt = static_cast((static_cast(i) / totalPoints) * 100.0f); - std::string ss = fmt::format("Level '{}' of '{}' || Processing Data '{}'% completed", (startLevel - currentLevel) + 1, startLevel - m_InputValues->Level, progressInt); - m_MessageHandler({IFilter::Message::Type::Info, ss}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { + return fmt::format("Level '{}' of '{}' || Processing Data '{}'% completed", (startLevel - currentLevel) + 1, startLevel - m_InputValues->Level, CalculatePercentCompleteAsInt(i, totalPoints)); + }); if(confidenceIndex[i] < m_InputValues->MinConfidence) { @@ -293,7 +273,7 @@ Result<> NeighborOrientationCorrelation::operator()() } } - if(getCancel()) + if(m_ShouldCancel) { return {}; } @@ -306,7 +286,7 @@ Result<> NeighborOrientationCorrelation::operator()() ParallelTaskAlgorithm parallelTask; for(const auto& dataArrayPtr : voxelArrays) { - parallelTask.execute(NeighborOrientationCorrelationTransferDataImpl(this, totalPoints, bestNeighbor, dataArrayPtr)); + parallelTask.execute(NeighborOrientationCorrelationTransferDataImpl(messageHelper, totalPoints, bestNeighbor, dataArrayPtr)); } currentLevel = currentLevel - 1; diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.hpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.hpp index d0f4871d11..0b21553a6d 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.hpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.hpp @@ -40,9 +40,6 @@ class ORIENTATIONANALYSIS_EXPORT NeighborOrientationCorrelation Result<> operator()(); - const std::atomic_bool& getCancel(); - void updateProgress(const std::string& progMessage); - private: DataStructure& m_DataStructure; const NeighborOrientationCorrelationInputValues* m_InputValues = nullptr; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/AlignSectionsFeatureCentroid.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/AlignSectionsFeatureCentroid.cpp index aa8bea4017..93a2cba98d 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/AlignSectionsFeatureCentroid.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/AlignSectionsFeatureCentroid.cpp @@ -69,7 +69,7 @@ Result<> AlignSectionsFeatureCentroid::findShifts(std::vector& xShifts, std::vector xCentroid(dims[2], 0.0f); std::vector yCentroid(dims[2], 0.0f); - auto start = std::chrono::steady_clock::now(); + ThrottledMessenger throttledMessenger = getMessageHelper().createThrottledMessenger(); // Loop over the Z Direction for(size_t iter = 0; iter < dims[2]; iter++) { @@ -77,15 +77,7 @@ Result<> AlignSectionsFeatureCentroid::findShifts(std::vector& xShifts, { return {}; } - progInt = static_cast(iter) / static_cast(dims[2]) * 100.0f; - auto now = std::chrono::steady_clock::now(); - // Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - std::string message = fmt::format("Determining Shifts || {}% Complete", progInt); - m_MessageHandler(nx::core::IFilter::ProgressMessage{nx::core::IFilter::Message::Type::Info, message, progInt}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Shifts || {}% Complete", CalculatePercentCompleteAsInt(iter, dims[2])); }); size_t count = 0; xCentroid[iter] = 0; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CalculateTriangleGroupCurvatures.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CalculateTriangleGroupCurvatures.cpp index 5fb5503fa7..5d87dbbc9f 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CalculateTriangleGroupCurvatures.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CalculateTriangleGroupCurvatures.cpp @@ -118,7 +118,8 @@ CalculateTriangleGroupCurvatures::CalculateTriangleGroupCurvatures(FeatureFaceCu Float64Array* principleCurvature1, Float64Array* principleCurvature2, Float64Array* principleDirection1, Float64Array* principleDirection2, Float64Array* gaussianCurvature, Float64Array* meanCurvature, Float64Array* weingartenMatrix, TriangleGeom* trianglesGeom, Int32Array* surfaceMeshFaceLabels, Float64Array* surfaceMeshFaceNormals, - Float64Array* surfaceMeshTriangleCentroids, const IFilter::MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) + Float64Array* surfaceMeshTriangleCentroids, const IFilter::MessageHandler& messageHandler, const std::atomic_bool& shouldCancel, + ProgressMessageHelper& progressMessageHelper) : m_Filter(filter) , m_NRing(nring) , m_TriangleIds(std::move(triangleIds)) @@ -136,6 +137,7 @@ CalculateTriangleGroupCurvatures::CalculateTriangleGroupCurvatures(FeatureFaceCu , m_SurfaceMeshTriangleCentroids(surfaceMeshTriangleCentroids) , m_MessageHandler(messageHandler) , m_ShouldCancel(shouldCancel) +, m_ProgressMessageHelper(progressMessageHelper) { } @@ -163,6 +165,8 @@ void CalculateTriangleGroupCurvatures::operator()() const return; } + ProgressMessenger progressMessenger = m_ProgressMessageHelper.createProgressMessenger(); + // Instantiate a FindNRingNeighbors class to use during the loop auto& faceLabels = m_SurfaceMeshFaceLabels->getDataStoreRef(); usize triangleIdOffset = m_TriangleIds[0] * 2; @@ -394,6 +398,6 @@ void CalculateTriangleGroupCurvatures::operator()() const } // End Loop over this triangle // Send some feedback - m_Filter->sendThreadSafeProgressMessage(1); + progressMessenger.sendProgressMessage(1, [&](usize currentProgress, usize maxProgress) { return fmt::format("{}/{}", currentProgress, maxProgress); }); } } // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CalculateTriangleGroupCurvatures.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CalculateTriangleGroupCurvatures.hpp index 1e8fd744f9..fb12deb1b9 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CalculateTriangleGroupCurvatures.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/CalculateTriangleGroupCurvatures.hpp @@ -40,6 +40,7 @@ #include "simplnx/DataStructure/DataArray.hpp" #include "simplnx/DataStructure/Geometry/TriangleGeom.hpp" #include "simplnx/Filter/IFilter.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "SimplnxCore/Filters/Algorithms/FeatureFaceCurvature.hpp" #include "SimplnxCore/SimplnxCore_export.hpp" @@ -57,7 +58,8 @@ class SIMPLNXCORE_EXPORT CalculateTriangleGroupCurvatures CalculateTriangleGroupCurvatures(FeatureFaceCurvature* filter, int64_t nring, std::vector triangleIds, bool useNormalsForCurveFitting, Float64Array* principleCurvature1, Float64Array* principleCurvature2, Float64Array* principleDirection1, Float64Array* principleDirection2, Float64Array* gaussianCurvature, Float64Array* meanCurvature, Float64Array* weingartenMatrix, TriangleGeom* trianglesGeom, Int32Array* surfaceMeshFaceLabels, Float64Array* surfaceMeshFaceNormals, - Float64Array* surfaceMeshTriangleCentroids, const IFilter::MessageHandler& messageHandler, const std::atomic_bool& shouldCancel); + Float64Array* surfaceMeshTriangleCentroids, const IFilter::MessageHandler& messageHandler, const std::atomic_bool& shouldCancel, + ProgressMessageHelper& progressMessageHelper); virtual ~CalculateTriangleGroupCurvatures(); @@ -83,5 +85,6 @@ class SIMPLNXCORE_EXPORT CalculateTriangleGroupCurvatures Float64Array* m_SurfaceMeshTriangleCentroids; const IFilter::MessageHandler& m_MessageHandler; const std::atomic_bool& m_ShouldCancel; + ProgressMessageHelper& m_ProgressMessageHelper; }; } // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp index 95dd4ba80c..06dacebad6 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp @@ -3,12 +3,13 @@ #include "SimplnxCore/Filters/ComputeArrayHistogramByFeatureFilter.hpp" #include "simplnx/DataStructure/DataArray.hpp" #include "simplnx/DataStructure/DataGroup.hpp" +#include "simplnx/DataStructure/INeighborList.hpp" #include "simplnx/Utilities/HistogramUtilities.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelAlgorithmUtilities.hpp" +#include "simplnx/Utilities/ParallelDataAlgorithm.hpp" #include "simplnx/Utilities/ParallelTaskAlgorithm.hpp" -#include -#include #include using namespace nx::core; @@ -37,7 +38,7 @@ class GenerateFeatureHistogramImpl GenerateFeatureHistogramImpl(const AbstractDataStore& inputStore, AbstractDataStore& binRangesStore, NeighborList* modalBinRangesList, const AbstractDataStore& featureIdsStore, float64 histMin, float64 histMax, bool histFullRange, const std::atomic_bool& shouldCancel, const int32 numBins, AbstractDataStore& histogramStore, AbstractDataStore& mostPopulatedStore, const std::unique_ptr& mask, - std::atomic& overflow, std::atomic& calculatedFeatureCount, usize totalFeatureCount, ComputeArrayHistogramByFeature* filter) + std::atomic& overflow, ProgressMessageHelper& progressMessageHelper) : m_InputStore(inputStore) , m_ShouldCancel(shouldCancel) , m_NumBins(numBins) @@ -51,16 +52,14 @@ class GenerateFeatureHistogramImpl , m_FeatureIdsStore(featureIdsStore) , m_Mask(mask) , m_Overflow(overflow) - , m_CalculatedFeatureCount(calculatedFeatureCount) - , m_TotalFeatureCount(totalFeatureCount) - , m_Filter(filter) + , m_ProgressMessageHelper(progressMessageHelper) { } GenerateFeatureHistogramImpl(const AbstractDataStore& inputStore, AbstractDataStore& binRangesStore, const AbstractDataStore& featureIdsStore, float64 histMin, float64 histMax, bool histFullRange, const std::atomic_bool& shouldCancel, const int32 numBins, AbstractDataStore& histogramStore, AbstractDataStore& mostPopulatedStore, const std::unique_ptr& mask, std::atomic& overflow, - std::atomic& calculatedFeatureCount, usize totalFeatureCount, ComputeArrayHistogramByFeature* filter) + ProgressMessageHelper& progressMessageHelper) : m_InputStore(inputStore) , m_ShouldCancel(shouldCancel) , m_NumBins(numBins) @@ -74,9 +73,7 @@ class GenerateFeatureHistogramImpl , m_FeatureIdsStore(featureIdsStore) , m_Mask(mask) , m_Overflow(overflow) - , m_CalculatedFeatureCount(calculatedFeatureCount) - , m_TotalFeatureCount(totalFeatureCount) - , m_Filter(filter) + , m_ProgressMessageHelper(progressMessageHelper) { } @@ -93,9 +90,7 @@ class GenerateFeatureHistogramImpl void compute(usize start, usize end) const { - std::chrono::steady_clock::time_point initialTime = std::chrono::steady_clock::now(); - auto now = std::chrono::steady_clock::now(); - const usize milliDelay = 1000; + ProgressMessenger progressMessenger = m_ProgressMessageHelper.createProgressMessenger(); const usize numTuples = m_FeatureIdsStore.getNumberOfTuples(); const usize numCurrentFeatures = end - start; @@ -163,7 +158,7 @@ class GenerateFeatureHistogramImpl m_Overflow++; } } // end of numTuples loop - } // end of increment else + } // end of increment else // Bool breaks neighbor lists; if we have made it here we know m_ModalBinRangesList is a nullptr if constexpr(!std::is_same_v) @@ -215,20 +210,19 @@ class GenerateFeatureHistogramImpl m_MostPopulatedStore.setComponent(j, 0, index); m_MostPopulatedStore.setComponent(j, 1, histogram[index]); - m_CalculatedFeatureCount++; - progressCount++; - now = std::chrono::steady_clock::now(); - if(progressCount > progressIncrement && std::chrono::duration_cast(now - initialTime).count() > milliDelay) + if(progressCount > progressIncrement) { - m_Filter->sendThreadSafeProgressMessage(m_CalculatedFeatureCount.load(), m_TotalFeatureCount); + progressMessenger.sendProgressMessage(progressCount, [&](usize currentProgress, usize maxProgress) { + int32 percentComplete = CalculatePercentCompleteAsInt(currentProgress, maxProgress); + return fmt::format("Calculating feature histograms {}/{}", currentProgress, maxProgress); + }); progressCount = 0; - initialTime = std::chrono::steady_clock::now(); } } // Send one at the end so that the progress is communicated properly - m_Filter->sendThreadSafeProgressMessage(m_CalculatedFeatureCount.load(), m_TotalFeatureCount); + progressMessenger.sendProgressMessage(progressCount); } private: @@ -245,9 +239,7 @@ class GenerateFeatureHistogramImpl AbstractDataStore& m_MostPopulatedStore; NeighborList* m_ModalBinRangesList; std::atomic& m_Overflow; - ComputeArrayHistogramByFeature* m_Filter = nullptr; - std::atomic& m_CalculatedFeatureCount; - usize m_TotalFeatureCount; + ProgressMessageHelper& m_ProgressMessageHelper; }; /** @@ -284,12 +276,6 @@ ComputeArrayHistogramByFeature::ComputeArrayHistogramByFeature(DataStructure& da // ----------------------------------------------------------------------------- ComputeArrayHistogramByFeature::~ComputeArrayHistogramByFeature() noexcept = default; -// ----------------------------------------------------------------------------- -const std::atomic_bool& ComputeArrayHistogramByFeature::getCancel() -{ - return m_ShouldCancel; -} - // ----------------------------------------------------------------------------- Result<> ComputeArrayHistogramByFeature::operator()() { @@ -302,6 +288,8 @@ Result<> ComputeArrayHistogramByFeature::operator()() usize numFeatures = *std::max_element(featureIdsStore.begin(), featureIdsStore.end()) + 1; + MessageHelper messageHelper(m_MessageHandler); + for(int32 i = 0; i < selectedArrayPaths.size(); i++) { if(m_ShouldCancel) @@ -328,7 +316,9 @@ Result<> ComputeArrayHistogramByFeature::operator()() dataAlg.setRange(0, numFeatures); bool histFullRange = !m_InputValues->UserDefinedRange; - std::atomic calculatedFeatureCount = 0; + + ProgressMessageHelper progressMessageHelper = messageHelper.createProgressMessageHelper(); + progressMessageHelper.setMaxProgresss(numFeatures); if(m_InputValues->CreatedBinModalRangesDataPaths.has_value()) { @@ -337,48 +327,21 @@ Result<> ComputeArrayHistogramByFeature::operator()() modalBinRanges->resizeTuples({numFeatures}); ExecuteParallelFunctor(InstantiateHistogramByFeatureImplFunctor{}, inputData->getDataType(), dataAlg, modalBinRanges, inputData, binRanges, featureIdsStore, m_InputValues->MinRange, m_InputValues->MaxRange, histFullRange, m_ShouldCancel, - numBins, counts, mostPopulated, mask, overflow, calculatedFeatureCount, numFeatures, this); + numBins, counts, mostPopulated, mask, overflow, progressMessageHelper); } else { ExecuteParallelFunctor(InstantiateHistogramByFeatureImplFunctor{}, inputData->getDataType(), dataAlg, inputData, binRanges, featureIdsStore, m_InputValues->MinRange, m_InputValues->MaxRange, - histFullRange, m_ShouldCancel, numBins, counts, mostPopulated, mask, overflow, calculatedFeatureCount, numFeatures, this); + histFullRange, m_ShouldCancel, numBins, counts, mostPopulated, mask, overflow, progressMessageHelper); } - m_MessageHandler(IFilter::Message::Type::Info, fmt::format("Calculated {} feature histograms!", numFeatures, numFeatures)); + messageHelper.sendMessage(fmt::format("Calculated {} feature histograms!", numFeatures)); if(overflow > 0) { - const std::string arrayName = inputData->getName(); - ComputeArrayHistogramByFeature::sendThreadSafeInfoMessage(fmt::format("{} values not categorized into bin for array {}", overflow.load(), arrayName)); + messageHelper.sendMessage(fmt::format("{} values not categorized into bin for array {}", overflow.load(), inputData->getName())); } } return {}; } - -// ----------------------------------------------------------------------------- -void ComputeArrayHistogramByFeature::sendThreadSafeInfoMessage(const std::string& message) -{ - const std::lock_guard guard(m_ProgressMessage_Mutex); - - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - m_InitialTime).count() > m_MilliDelay) - { - m_MessageHandler(IFilter::Message::Type::Info, message); - m_InitialTime = std::chrono::steady_clock::now(); - } -} - -// ----------------------------------------------------------------------------- -void ComputeArrayHistogramByFeature::sendThreadSafeProgressMessage(usize calculatedFeatureCount, usize totalFeatures) -{ - const std::lock_guard guard(m_ProgressMessage_Mutex); - - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - m_InitialTime).count() > m_MilliDelay) - { - m_MessageHandler(IFilter::Message::Type::Info, fmt::format("Calculating feature histograms {}/{}", calculatedFeatureCount, totalFeatures)); - m_InitialTime = std::chrono::steady_clock::now(); - } -} diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.hpp index 5a6b2196b6..93c99e45ac 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.hpp @@ -42,20 +42,10 @@ class SIMPLNXCORE_EXPORT ComputeArrayHistogramByFeature Result<> operator()(); - void sendThreadSafeProgressMessage(usize calculatedFeatureCount, usize totalFeatures); - void sendThreadSafeInfoMessage(const std::string& message); - - const std::atomic_bool& getCancel(); - private: DataStructure& m_DataStructure; const ComputeArrayHistogramByFeatureInputValues* m_InputValues = nullptr; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; - - // Thread safe Progress Message - mutable std::mutex m_ProgressMessage_Mutex; - usize m_MilliDelay = 1000; - std::chrono::steady_clock::time_point m_InitialTime = std::chrono::steady_clock::now(); }; } // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp index 1f70562bb3..fbedd7d735 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp @@ -6,6 +6,7 @@ #include "simplnx/Utilities/HistogramUtilities.hpp" #include "simplnx/Utilities/MaskCompareUtilities.hpp" #include "simplnx/Utilities/Math/StatisticsCalculations.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelDataAlgorithm.hpp" #include @@ -45,7 +46,8 @@ class StatisticsByFeatureImpl public: StatisticsByFeatureImpl(bool length, bool min, bool max, bool mean, bool mode, bool stdDeviation, bool summation, const std::unique_ptr& mask, const Int32AbstractDataStore& featureIds, const AbstractDataStore& source, BoolArray* featureHasDataArray, UInt64Array* lengthArray, DataArray* minArray, - DataArray* maxArray, Float32Array* meanArray, NeighborList* modeArray, Float32Array* stdDevArray, Float32Array* summationArray, ComputeArrayStatistics* filter) + DataArray* maxArray, Float32Array* meanArray, NeighborList* modeArray, Float32Array* stdDevArray, Float32Array* summationArray, const std::atomic_bool& shouldCancel, + MessageHelper& messageHelper) : m_Length(length) , m_Min(min) , m_Max(max) @@ -64,33 +66,30 @@ class StatisticsByFeatureImpl , m_ModeArray(modeArray) , m_StdDevArray(stdDevArray) , m_SummationArray(summationArray) - , m_Filter(filter) + , m_ShouldCancel(shouldCancel) + , m_MessageHelper(messageHelper) { } void compute(usize start, usize end) const { - std::chrono::steady_clock::time_point initialTime = std::chrono::steady_clock::now(); - auto now = std::chrono::steady_clock::now(); - const usize milliDelay = 1000; + ThrottledMessenger throttledMessenger = m_MessageHelper.createThrottledMessenger(); - const std::atomic_bool& shouldCancel = m_Filter->getCancel(); const usize numTuples = m_FeatureIds.getNumberOfTuples(); const usize numCurrentFeatures = end - start; - auto msgHandler = [this](const std::string& msg) { m_Filter->sendThreadSafeInfoMessage("Preparing features/ensembles for stats calculation " + msg); }; - auto [length, min, max, summation, modalMaps] = HistogramUtilities::concurrent::CalculateFeatureHasDataStats(m_Source, m_FeatureIds, start, end, m_Mask, msgHandler, shouldCancel); - if(shouldCancel) + auto msgHandler = [this](const std::string& msg) { m_MessageHelper.trySendMessage("Preparing features/ensembles for stats calculation " + msg); }; + auto [length, min, max, summation, modalMaps] = HistogramUtilities::concurrent::CalculateFeatureHasDataStats(m_Source, m_FeatureIds, start, end, m_Mask, msgHandler, m_ShouldCancel); + if(m_ShouldCancel) { return; } usize progressCount = 0; - usize progressIncrement = numTuples / 100; + usize progressIncrement = numCurrentFeatures / 100; + + m_MessageHelper.sendMessage(fmt::format("Calculating statistics for feature range [{}-{}]", start, end)); - m_Filter->sendThreadSafeInfoMessage(fmt::format("Calculating statistics for feature range [{}-{}]", start, end)); - progressIncrement = numCurrentFeatures / 100; - progressCount = 0; std::vector meanArray; if(m_StdDeviation && !m_Mean) { @@ -98,7 +97,7 @@ class StatisticsByFeatureImpl } for(usize j = start; j < end; j++) { - if(shouldCancel) + if(m_ShouldCancel) { return; } @@ -164,26 +163,26 @@ class StatisticsByFeatureImpl } progressCount++; - now = std::chrono::steady_clock::now(); - if(progressCount > progressIncrement && std::chrono::duration_cast(now - initialTime).count() > milliDelay) + if(progressCount > progressIncrement) { - m_Filter->sendThreadSafeInfoMessage(fmt::format("Calculating statistics for feature [{}-{}] {}/{}", start, end, j, end)); - progressCount = 0; - initialTime = std::chrono::steady_clock::now(); + throttledMessenger.sendThrottledMessage([&]() { + progressCount = 0; + return fmt::format("Calculating statistics for feature [{}-{}] {}/{}", start, end, j, end); + }); } } if(m_StdDeviation) { // https://www.khanacademy.org/math/statistics-probability/summarizing-quantitative-data/variance-standard-deviation-population/a/calculating-standard-deviation-step-by-step - m_Filter->sendThreadSafeInfoMessage(fmt::format("Computing StdDev Feature/Ensemble [{}-{}]", start, end)); + m_MessageHelper.sendMessage(fmt::format("Computing StdDev Feature/Ensemble [{}-{}]", start, end)); // This should probably be done with Kahan Summation instead std::vector sumOfDiffs(numCurrentFeatures, 0.0f); progressCount = 0; for(usize tupleIndex = 0; tupleIndex < numTuples; tupleIndex++) { - if(shouldCancel) + if(m_ShouldCancel) { return; } @@ -203,12 +202,12 @@ class StatisticsByFeatureImpl sumOfDiffs[featureId - start] += static_cast((m_Source[tupleIndex] - meanVal) * (m_Source[tupleIndex] - meanVal)); progressCount++; - now = std::chrono::steady_clock::now(); - if(progressCount > progressIncrement && std::chrono::duration_cast(now - initialTime).count() > milliDelay) + if(progressCount > progressIncrement) { - m_Filter->sendThreadSafeInfoMessage(fmt::format("StdDev Calculation Feature/Ensemble [{}-{}]: {:.2f}%", start, end, 100.0f * static_cast(tupleIndex) / static_cast(numTuples))); - progressCount = 0; - initialTime = std::chrono::steady_clock::now(); + throttledMessenger.sendThrottledMessage([&]() { + progressCount = 0; + return fmt::format("StdDev Calculation Feature/Ensemble [{}-{}]: {:.2f}%", start, end, 100.0f * static_cast(tupleIndex) / static_cast(numTuples)); + }); } } @@ -246,7 +245,8 @@ class StatisticsByFeatureImpl NeighborList* m_ModeArray = nullptr; Float32Array* m_StdDevArray = nullptr; Float32Array* m_SummationArray = nullptr; - ComputeArrayStatistics* m_Filter = nullptr; + const std::atomic_bool& m_ShouldCancel; + MessageHelper& m_MessageHelper; }; template @@ -256,7 +256,7 @@ class StatisticsByFeatureRangeImpl StatisticsByFeatureRangeImpl(bool length, bool min, bool max, bool mean, bool mode, bool stdDeviation, bool summation, const Int32AbstractDataStore& featureIdsMap, const BoolAbstractDataStore& tempMask, const Int32AbstractDataStore& featureIds, const AbstractDataStore& source, BoolArray* featureHasDataArray, UInt64Array* lengthArray, DataArray* minArray, DataArray* maxArray, Float32Array* meanArray, NeighborList* modeArray, Float32Array* stdDevArray, - Float32Array* summationArray, ComputeArrayStatistics* filter) + Float32Array* summationArray, const std::atomic_bool& shouldCancel, MessageHelper& messageHelper) : m_Length(length) , m_Min(min) , m_Max(max) @@ -276,20 +276,18 @@ class StatisticsByFeatureRangeImpl , m_ModeArray(modeArray) , m_StdDevArray(stdDevArray) , m_SummationArray(summationArray) - , m_Filter(filter) + , m_ShouldCancel(shouldCancel) + , m_MessageHelper(messageHelper) { } void compute(usize start, usize end) const { - std::chrono::steady_clock::time_point initialTime = std::chrono::steady_clock::now(); - auto now = std::chrono::steady_clock::now(); - - const std::atomic_bool& shouldCancel = m_Filter->getCancel(); + ThrottledMessenger throttledMessenger = m_MessageHelper.createThrottledMessenger(); const usize numTuples = m_Source.getNumberOfTuples(); for(usize featureId = start; featureId < end; featureId++) { - if(shouldCancel) + if(m_ShouldCancel) { return; } @@ -398,12 +396,7 @@ class StatisticsByFeatureRangeImpl m_FeatureHasDataArray->initializeTuple(truePosition, false); } - now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - initialTime).count() > 1000) - { - m_Filter->sendThreadSafeInfoMessage(fmt::format("Storing data for feature/ensembles [{}-{}] {}/{}", start, end, featureId, end)); - initialTime = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Storing data for feature/ensembles [{}-{}] {}/{}", start, end, featureId, end); }); } } // end of compute @@ -432,7 +425,8 @@ class StatisticsByFeatureRangeImpl NeighborList* m_ModeArray = nullptr; Float32Array* m_StdDevArray = nullptr; Float32Array* m_SummationArray = nullptr; - ComputeArrayStatistics* m_Filter = nullptr; + const std::atomic_bool& m_ShouldCancel; + MessageHelper& m_MessageHelper; }; template @@ -440,7 +434,7 @@ class MedianByFeatureImpl { public: MedianByFeatureImpl(const std::unique_ptr& mask, const Int32AbstractDataStore& featureIds, const AbstractDataStore& source, bool findMedian, bool findNumUnique, - Float32Array* medianArray, Int32Array* numUniqueValuesArray, DataArray* lengthArray, ComputeArrayStatistics* filter) + Float32Array* medianArray, Int32Array* numUniqueValuesArray, DataArray* lengthArray, MessageHelper& messageHelper) : m_FindMedian(findMedian) , m_FindNumUniqueValues(findNumUnique) , m_MedianArray(medianArray) @@ -449,13 +443,13 @@ class MedianByFeatureImpl , m_FeatureIds(featureIds) , m_Source(source) , m_LengthArray(lengthArray) - , m_Filter(filter) + , m_MessageHelper(messageHelper) { } void compute(usize start, usize end) const { - m_Filter->sendThreadSafeInfoMessage(fmt::format("Starting Median Array Calculation: Feature/Ensemble [{}-{}]", start, end)); + m_MessageHelper.sendMessage(fmt::format("Starting Median Array Calculation: Feature/Ensemble [{}-{}]", start, end)); const usize numFeatureSources = end - start; // Create the arrays that will collect the values from the arrays. allocate them to the correct size based on the length array @@ -511,7 +505,7 @@ class MedianByFeatureImpl const Int32AbstractDataStore& m_FeatureIds; const AbstractDataStore& m_Source; const DataArray* m_LengthArray = nullptr; - ComputeArrayStatistics* m_Filter = nullptr; + MessageHelper& m_MessageHelper; }; template @@ -519,7 +513,7 @@ class MedianByFeatureRangeImpl { public: MedianByFeatureRangeImpl(const Int32AbstractDataStore& featureIdsMap, const BoolAbstractDataStore& tempMask, const Int32AbstractDataStore& featureIds, const AbstractDataStore& source, - bool findMedian, bool findNumUnique, Float32Array* medianArray, Int32Array* numUniqueValuesArray, ComputeArrayStatistics* filter) + bool findMedian, bool findNumUnique, Float32Array* medianArray, Int32Array* numUniqueValuesArray, MessageHelper& messageHelper) : m_FindMedian(findMedian) , m_FindNumUniqueValues(findNumUnique) , m_MedianArray(medianArray) @@ -528,13 +522,13 @@ class MedianByFeatureRangeImpl , m_Mask(tempMask) , m_FeatureIds(featureIds) , m_Source(source) - , m_Filter(filter) + , m_MessageHelper(messageHelper) { } void compute(usize start, usize end) const { - m_Filter->sendThreadSafeInfoMessage(fmt::format("Starting Median Array Calculation: Feature/Ensemble [{}-{}]", start, end)); + m_MessageHelper.sendMessage(fmt::format("Starting Median Array Calculation: Feature/Ensemble [{}-{}]", start, end)); const usize numTuples = m_Source.getNumberOfTuples(); for(usize featureId = start; featureId < end; featureId++) @@ -600,7 +594,7 @@ class MedianByFeatureRangeImpl const Int32AbstractDataStore& m_FeatureIds; const Int32AbstractDataStore& m_FeatureIdsMap; const AbstractDataStore& m_Source; - ComputeArrayStatistics* m_Filter = nullptr; + MessageHelper& m_MessageHelper; }; // ----------------------------------------------------------------------------- @@ -885,7 +879,7 @@ struct ComputeArrayStatisticsByFeatureFunctor { template Result<> operator()(DataStructure& dataStructure, const IDataArray* inputIDataArray, std::vector& arrays, usize numFeatures, const ComputeArrayStatisticsInputValues* inputValues, - ComputeArrayStatistics* filter) + const std::atomic_bool& shouldCancel, MessageHelper& messageHelper) { std::unique_ptr maskCompare = nullptr; if(inputValues->UseMask) @@ -935,7 +929,7 @@ struct ComputeArrayStatisticsByFeatureFunctor auto& data = inputArrayPtr->getDataStoreRef(); StatisticsByFeatureImpl classToExecute = StatisticsByFeatureImpl(inputValues->FindLength, inputValues->FindMin, inputValues->FindMax, inputValues->FindMean, inputValues->FindMode, inputValues->FindStdDeviation, inputValues->FindSummation, maskCompare, featureIds, data, featureHasDataPtr, lengthArrayPtr, - minArrayPtr, maxArrayPtr, meanArrayPtr, modeArrayPtr, stdDevArrayPtr, summationArrayPtr, filter); + minArrayPtr, maxArrayPtr, meanArrayPtr, modeArrayPtr, stdDevArrayPtr, summationArrayPtr, shouldCancel, messageHelper); if(CheckArraysInMemory(indexAlgArrays)) { const tbb::simple_partitioner simplePartitioner; @@ -953,7 +947,7 @@ struct ComputeArrayStatisticsByFeatureFunctor if(inputValues->FindMedian || inputValues->FindNumUniqueValues) { - filter->sendThreadSafeInfoMessage("Starting Median Calculation.."); + messageHelper.sendMessage("Starting Median Calculation..."); auto* medianArrayPtr = dynamic_cast(arrays[4]); auto* numUniqueValuesArrayPtr = dynamic_cast(arrays[8]); @@ -972,7 +966,7 @@ struct ComputeArrayStatisticsByFeatureFunctor } medianDataAlg.setRange(0, numFeatures); medianDataAlg.execute( - MedianByFeatureImpl(maskCompare, featureIds, data, inputValues->FindMedian, inputValues->FindNumUniqueValues, medianArrayPtr, numUniqueValuesArrayPtr, lengthArrayPtr, filter)); + MedianByFeatureImpl(maskCompare, featureIds, data, inputValues->FindMedian, inputValues->FindNumUniqueValues, medianArrayPtr, numUniqueValuesArrayPtr, lengthArrayPtr, messageHelper)); } // compute the standardized data @@ -996,7 +990,7 @@ struct ComputeArrayStatisticsByFeatureFunctor template Result<> operator()(DataStructure& dataStructure, const IDataArray* inputIDataArray, std::vector& arrays, const std::pair& range, - const ComputeArrayStatisticsInputValues* inputValues, ComputeArrayStatistics* filter) + const ComputeArrayStatisticsInputValues* inputValues, const std::atomic_bool& shouldCancel, MessageHelper& messageHelper) { Result<> initializationResult = InitializeArrays(dataStructure, inputValues); if(initializationResult.invalid()) @@ -1038,7 +1032,7 @@ struct ComputeArrayStatisticsByFeatureFunctor const auto& data = inputArrayPtr->getDataStoreRef(); StatisticsByFeatureRangeImpl classToExecute = StatisticsByFeatureRangeImpl( inputValues->FindLength, inputValues->FindMin, inputValues->FindMax, inputValues->FindMean, inputValues->FindMode, inputValues->FindStdDeviation, inputValues->FindSummation, featureIdsMap, - tempMask, featureIds, data, featureHasDataPtr, lengthArrayPtr, minArrayPtr, maxArrayPtr, meanArrayPtr, modeArrayPtr, stdDevArrayPtr, summationArrayPtr, filter); + tempMask, featureIds, data, featureHasDataPtr, lengthArrayPtr, minArrayPtr, maxArrayPtr, meanArrayPtr, modeArrayPtr, stdDevArrayPtr, summationArrayPtr, shouldCancel, messageHelper); if(CheckArraysInMemory(indexAlgArrays)) { const tbb::simple_partitioner simplePartitioner; @@ -1056,7 +1050,7 @@ struct ComputeArrayStatisticsByFeatureFunctor if(inputValues->FindMedian || inputValues->FindNumUniqueValues) { - filter->sendThreadSafeInfoMessage("Starting Median Calculation.."); + messageHelper.sendMessage("Starting Median Calculation..."); auto* medianArrayPtr = dynamic_cast(arrays[4]); auto* numUniqueValuesArrayPtr = dynamic_cast(arrays[8]); @@ -1074,7 +1068,7 @@ struct ComputeArrayStatisticsByFeatureFunctor } medianDataAlg.setRange(range.first, range.second + 1); medianDataAlg.execute( - MedianByFeatureRangeImpl(featureIdsMap, tempMask, featureIds, data, inputValues->FindMedian, inputValues->FindNumUniqueValues, medianArrayPtr, numUniqueValuesArrayPtr, filter)); + MedianByFeatureRangeImpl(featureIdsMap, tempMask, featureIds, data, inputValues->FindMedian, inputValues->FindNumUniqueValues, medianArrayPtr, numUniqueValuesArrayPtr, messageHelper)); } // compute the standardized data based on whether computing by index or not @@ -1160,6 +1154,8 @@ Result<> ComputeArrayStatistics::operator()() arrays[8] = m_DataStructure.getDataAs(m_InputValues->NumUniqueValuesName); } + MessageHelper messageHelper(m_MessageHandler); + if(!m_InputValues->ComputeByIndex) { const auto& inputArray = m_DataStructure.getDataRefAs(m_InputValues->SelectedArrayPath); @@ -1214,7 +1210,7 @@ Result<> ComputeArrayStatistics::operator()() const auto* inputArray = m_DataStructure.getDataAs(m_InputValues->SelectedArrayPath); // We must use ExecuteNeighborFunction because the Mode array is a NeighborList - return ExecuteNeighborFunction(ComputeArrayStatisticsByFeatureFunctor{}, inputArray->getDataType(), m_DataStructure, inputArray, arrays, numFeatures, m_InputValues, this); + return ExecuteNeighborFunction(ComputeArrayStatisticsByFeatureFunctor{}, inputArray->getDataType(), m_DataStructure, inputArray, arrays, numFeatures, m_InputValues, this, messageHelper); } default: { return MakeErrorResult(-506670, fmt::format("Unknown feature id range controls option selected!", trueMin, trueMax)); @@ -1287,40 +1283,5 @@ Result<> ComputeArrayStatistics::operator()() const auto* inputArray = m_DataStructure.getDataAs(m_InputValues->SelectedArrayPath); // We must use ExecuteNeighborFunction because the Mode array is a NeighborList - return ExecuteNeighborFunction(ComputeArrayStatisticsByFeatureFunctor{}, inputArray->getDataType(), m_DataStructure, inputArray, arrays, std::make_pair(trueMin, trueMax), m_InputValues, this); -} - -// ----------------------------------------------------------------------------- -const std::atomic_bool& ComputeArrayStatistics::getCancel() -{ - return m_ShouldCancel; -} - -// ----------------------------------------------------------------------------- -void ComputeArrayStatistics::sendThreadSafeProgressMessage(usize counter) -{ - const std::lock_guard guard(m_ProgressMessage_Mutex); - - m_ProgressCounter += counter; - auto progressInt = static_cast((static_cast(m_ProgressCounter) / static_cast(m_TotalElements)) * 100.0f); - - if(m_ProgressCounter > 1 && m_LastProgressInt != progressInt) - { - m_MessageHandler(IFilter::Message::Type::Info, fmt::format("Finding Distances || {}% Completed", progressInt)); - } - - m_LastProgressInt = progressInt; -} - -// ----------------------------------------------------------------------------- -void ComputeArrayStatistics::sendThreadSafeInfoMessage(const std::string& message) -{ - const std::lock_guard guard(m_ProgressMessage_Mutex); - - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - m_InitialTime).count() > m_MilliDelay) - { - m_MessageHandler(IFilter::Message::Type::Info, message); - m_InitialTime = std::chrono::steady_clock::now(); - } + return ExecuteNeighborFunction(ComputeArrayStatisticsByFeatureFunctor{}, inputArray->getDataType(), m_DataStructure, inputArray, arrays, std::make_pair(trueMin, trueMax), m_InputValues, m_ShouldCancel, messageHelper); } diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.hpp index 8cfb38b9ce..a754fd4d90 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.hpp @@ -80,24 +80,11 @@ class SIMPLNXCORE_EXPORT ComputeArrayStatistics Result<> operator()(); - const std::atomic_bool& getCancel(); - - void sendThreadSafeProgressMessage(usize counter); - void sendThreadSafeInfoMessage(const std::string& message); - private: DataStructure& m_DataStructure; const ComputeArrayStatisticsInputValues* m_InputValues = nullptr; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; - - // Thread safe Progress Message - mutable std::mutex m_ProgressMessage_Mutex; - usize m_MilliDelay = 1000; - size_t m_TotalElements = 0; - size_t m_ProgressCounter = 0; - size_t m_LastProgressInt = 0; - std::chrono::steady_clock::time_point m_InitialTime = std::chrono::steady_clock::now(); }; } // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.cpp index b759c1ccc3..b598732d61 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.cpp @@ -3,6 +3,7 @@ #include "simplnx/Utilities/DataArrayUtilities.hpp" #include "simplnx/Utilities/FilterUtilities.hpp" #include "simplnx/Utilities/Math/StatisticsCalculations.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelDataAlgorithm.hpp" using namespace nx::core; @@ -21,7 +22,7 @@ class ComputeNeighborListStatisticsImpl using StoreType = AbstractDataStore; ComputeNeighborListStatisticsImpl(ComputeNeighborListStatistics* filter, const INeighborList& source, bool length, bool min, bool max, bool mean, bool median, bool stdDeviation, bool summation, - std::vector& arrays, const std::atomic_bool& shouldCancel) + std::vector& arrays, const std::atomic_bool& shouldCancel, ProgressMessageHelper& progressMessageHelper) : m_Filter(filter) , m_ShouldCancel(shouldCancel) , m_Source(source) @@ -33,6 +34,7 @@ class ComputeNeighborListStatisticsImpl , m_StdDeviation(stdDeviation) , m_Summation(summation) , m_Arrays(arrays) + , m_ProgressMessageHelper(progressMessageHelper) { } @@ -78,8 +80,7 @@ class ComputeNeighborListStatisticsImpl const auto& sourceList = dynamic_cast(m_Source); - auto tStart = std::chrono::steady_clock::now(); - usize counter = 0; + ProgressMessenger progressMessenger = m_ProgressMessageHelper.createProgressMessenger(); for(usize i = start; i < end; i++) { if(m_ShouldCancel) @@ -125,14 +126,8 @@ class ComputeNeighborListStatisticsImpl array6->setValue(i, val); } - counter++; - if(std::chrono::duration_cast(std::chrono::steady_clock::now() - tStart).count() > 1000) - { - m_Filter->sendThreadSafeProgressMessage(counter); - counter = 0; - } + progressMessenger.sendProgressMessage(1); } - m_Filter->sendThreadSafeProgressMessage(counter); } void operator()(const Range& range) const @@ -154,6 +149,7 @@ class ComputeNeighborListStatisticsImpl bool m_Summation = false; std::vector& m_Arrays; + ProgressMessageHelper& m_ProgressMessageHelper; }; } // namespace @@ -218,16 +214,17 @@ Result<> ComputeNeighborListStatistics::operator()() arrays[6] = m_DataStructure.getDataAs(m_InputValues->SummationPath); } - // Fill progress counters for parallel updates - m_ProgressCounter = 0; - m_TotalElements = numTuples; + MessageHelper messageHelper(m_MessageHandler); + ProgressMessageHelper progresssMessageHelper = messageHelper.createProgressMessageHelper(); + progresssMessageHelper.setMaxProgresss(numTuples); + progresssMessageHelper.setProgressMessageTemplate("Finding Statistics || {}% Completed"); // Allow data-based parallelization ParallelDataAlgorithm dataAlg; dataAlg.setRange(0, numTuples); ExecuteParallelFunction(type, dataAlg, this, inputINeighborList, m_InputValues->FindLength, m_InputValues->FindMin, m_InputValues->FindMax, m_InputValues->FindMean, m_InputValues->FindMedian, m_InputValues->FindStdDeviation, m_InputValues->FindSummation, arrays, - m_ShouldCancel); + m_ShouldCancel, progresssMessageHelper); return {}; } @@ -237,18 +234,3 @@ const std::atomic_bool& ComputeNeighborListStatistics::getCancel() { return m_ShouldCancel; } - -// ----------------------------------------------------------------------------- -void ComputeNeighborListStatistics::sendThreadSafeProgressMessage(usize counter) -{ - const std::lock_guard guard(m_ProgressMessage_Mutex); - - m_ProgressCounter += counter; - - if(std::chrono::duration_cast(std::chrono::steady_clock::now() - m_InitialTime).count() > 1000) - { - auto progressInt = static_cast((static_cast(m_ProgressCounter) / static_cast(m_TotalElements)) * 100.0); - m_MessageHandler(IFilter::Message::Type::Info, fmt::format("Finding Statistics || {}% Completed", progressInt)); - m_InitialTime = std::chrono::steady_clock::now(); - } -} diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.hpp index 8b3e5a73c0..4f65dbd8ff 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.hpp @@ -52,19 +52,11 @@ class SIMPLNXCORE_EXPORT ComputeNeighborListStatistics const std::atomic_bool& getCancel(); - void sendThreadSafeProgressMessage(usize counter); - private: DataStructure& m_DataStructure; const ComputeNeighborListStatisticsInputValues* m_InputValues = nullptr; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; - - // Thread safe Progress Message - mutable std::mutex m_ProgressMessage_Mutex; - size_t m_TotalElements = 0; - size_t m_ProgressCounter = 0; - std::chrono::steady_clock::time_point m_InitialTime = std::chrono::steady_clock::now(); }; } // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp index 5059ced486..2ff0b68f0b 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp @@ -13,12 +13,13 @@ namespace class ComputeNeighborhoodsImpl { public: - ComputeNeighborhoodsImpl(ComputeNeighborhoods* filter, usize totalFeatures, const std::vector& bins, const std::vector& criticalDistance, const std::atomic_bool& shouldCancel) + ComputeNeighborhoodsImpl(ComputeNeighborhoods* filter, usize totalFeatures, const std::vector& bins, const std::vector& criticalDistance, const std::atomic_bool& shouldCancel, ProgressMessageHelper& progressMessageHelper) : m_Filter(filter) , m_TotalFeatures(totalFeatures) , m_Bins(bins) , m_CriticalDistance(criticalDistance) , m_ShouldCancel(shouldCancel) + , m_ProgressMessageHelper(progressMessageHelper) { } @@ -36,19 +37,17 @@ class ComputeNeighborhoodsImpl start = 1; } - auto startTime = std::chrono::steady_clock::now(); + ProgressMessenger progressMessenger = m_ProgressMessageHelper.createProgressMessenger(); for(usize featureIdx = start; featureIdx < end; featureIdx++) { incCount++; if(incCount >= increment) { - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - startTime).count() > 1000) - { - incCount = 0; - m_Filter->updateProgress(incCount, now); - startTime = now; - } + progressMessenger.sendProgressMessage(incCount, [&](usize currentProgress, usize maxProgress) { + int32 percentComplete = CalculatePercentCompleteAsInt(currentProgress, maxProgress); + return fmt::format("Calculating feature histograms {}/{}", currentProgress, maxProgress); + }); + incCount = 0; } if(m_ShouldCancel) @@ -83,7 +82,7 @@ class ComputeNeighborhoodsImpl } } } - m_Filter->updateProgress(incCount); + progressMessenger.sendProgressMessage(incCount); } void operator()(const Range& range) const @@ -97,6 +96,7 @@ class ComputeNeighborhoodsImpl const std::vector& m_Bins; const std::vector& m_CriticalDistance; const std::atomic_bool& m_ShouldCancel; + ProgressMessageHelper& m_ProgressMessageHelper; }; } // namespace @@ -106,6 +106,7 @@ ComputeNeighborhoods::ComputeNeighborhoods(DataStructure& dataStructure, const I , m_InputValues(inputValues) , m_ShouldCancel(shouldCancel) , m_MessageHandler(mesgHandler) +, m_MessageHelper(m_MessageHandler) { } @@ -126,24 +127,6 @@ void ComputeNeighborhoods::updateNeighborHood(usize sourceIndex, usize destIndex m_LocalNeighborhoodList[sourceIndex].push_back(static_cast(destIndex)); } -// ----------------------------------------------------------------------------- -// -// ----------------------------------------------------------------------------- -void ComputeNeighborhoods::updateProgress(float64 counter, const std::chrono::steady_clock::time_point& now) -{ - const std::lock_guard lock(m_Mutex); - - m_ProgressCounter += counter; - - if(std::chrono::duration_cast(now - m_InitialTime).count() > 1000) // every second update - { - auto progressInt = static_cast((m_ProgressCounter / m_TotalFeatures) * 100.0); - std::string progressMessage = "Finding Feature Neighborhoods:"; - m_MessageHandler(IFilter::ProgressMessage{IFilter::Message::Type::Progress, progressMessage, progressInt}); - m_InitialTime = std::chrono::steady_clock::now(); - } -} - // ----------------------------------------------------------------------------- Result<> ComputeNeighborhoods::operator()() { @@ -157,7 +140,10 @@ Result<> ComputeNeighborhoods::operator()() m_Neighborhoods = m_DataStructure.getDataAs(m_InputValues->NeighborhoodsArrayName); usize totalFeatures = equivalentDiameters.getNumberOfTuples(); - m_TotalFeatures = static_cast(totalFeatures); // Pre-cast to save time in lock later + + ProgressMessageHelper progressMessageHelper = m_MessageHelper.createProgressMessageHelper(); + progressMessageHelper.setMaxProgresss(totalFeatures); + progressMessageHelper.setProgressMessageTemplate("Finding Feature Neighborhoods: {}"); m_LocalNeighborhoodList.resize(totalFeatures); criticalDistance.resize(totalFeatures); @@ -190,7 +176,7 @@ Result<> ComputeNeighborhoods::operator()() ParallelDataAlgorithm parallelAlgorithm; parallelAlgorithm.setRange(Range(0, totalFeatures)); parallelAlgorithm.setParallelizationEnabled(true); - parallelAlgorithm.execute(ComputeNeighborhoodsImpl(this, totalFeatures, bins, criticalDistance, m_ShouldCancel)); + parallelAlgorithm.execute(ComputeNeighborhoodsImpl(this, totalFeatures, bins, criticalDistance, m_ShouldCancel, progressMessageHelper)); // Output Variables auto& outputNeighborList = m_DataStructure.getDataRefAs>(m_InputValues->NeighborhoodListArrayName); diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.hpp index 9824f6f127..8f11b26d8a 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.hpp @@ -7,6 +7,7 @@ #include "simplnx/DataStructure/DataStructure.hpp" #include "simplnx/DataStructure/NeighborList.hpp" #include "simplnx/Filter/IFilter.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include @@ -43,19 +44,14 @@ class SIMPLNXCORE_EXPORT ComputeNeighborhoods const std::atomic_bool& getCancel(); void updateNeighborHood(usize sourceIndex, usize targetIndex); - void updateProgress(float64 counter, const std::chrono::steady_clock::time_point& now = std::chrono::steady_clock::now()); private: DataStructure& m_DataStructure; const ComputeNeighborhoodsInputValues* m_InputValues = nullptr; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; - std::mutex m_Mutex; - std::chrono::steady_clock::time_point m_InitialTime = std::chrono::steady_clock::now(); - float64 m_TotalFeatures = 0; - float64 m_ProgressCounter = 0; - + MessageHelper m_MessageHelper; Int32Array* m_Neighborhoods = nullptr; std::vector> m_LocalNeighborhoodList; }; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.cpp index 24ad9003ef..cfa4feab0e 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.cpp @@ -4,6 +4,7 @@ #include "simplnx/DataStructure/DataGroup.hpp" #include "simplnx/DataStructure/Geometry/TriangleGeom.hpp" #include "simplnx/DataStructure/Geometry/VertexGeom.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelDataAlgorithm.hpp" #include "simplnx/Utilities/RTree.hpp" @@ -305,7 +306,8 @@ class ComputeVertexToTriangleDistancesImpl { public: ComputeVertexToTriangleDistancesImpl(ComputeVertexToTriangleDistances* filter, const SharedTriListT& triangles, const SharedVertexListT& vertices, SharedVertexListT& sourcePoints, - Float32AbstractDataStore& distances, Int64AbstractDataStore& closestTri, const Float64AbstractDataStore& normals, const RTreeType rtree) + Float32AbstractDataStore& distances, Int64AbstractDataStore& closestTri, const Float64AbstractDataStore& normals, const RTreeType rtree, + ProgressMessageHelper& progressMessageHelper) : m_Filter(filter) , m_SharedTriangleList(triangles) , m_TriangleVertices(vertices) @@ -314,6 +316,7 @@ class ComputeVertexToTriangleDistancesImpl , m_ClosestTri(closestTri) , m_Normals(normals) , m_RTree(rtree) + , m_ProgressMessageHelper(progressMessageHelper) { } virtual ~ComputeVertexToTriangleDistancesImpl() = default; @@ -325,6 +328,8 @@ class ComputeVertexToTriangleDistancesImpl void compute(usize start, usize end) const { + ProgressMessenger progressMessenger = m_ProgressMessageHelper.createProgressMessenger(); + int64 counter = 0; auto progIncrement = static_cast((end - start) / 100); @@ -406,12 +411,12 @@ class ComputeVertexToTriangleDistancesImpl if(counter > progIncrement) { - m_Filter->sendThreadSafeProgressMessage(counter); + progressMessenger.sendProgressMessage(counter); counter = 0; } counter++; } - m_Filter->sendThreadSafeProgressMessage(counter); + progressMessenger.sendProgressMessage(counter); } private: @@ -423,6 +428,7 @@ class ComputeVertexToTriangleDistancesImpl Int64AbstractDataStore& m_ClosestTri; const Float64AbstractDataStore& m_Normals; const RTreeType m_RTree; + ProgressMessageHelper& m_ProgressMessageHelper; }; void GetBoundingBoxAtTri(const SharedTriListT& triList, const SharedVertexListT& vertList, size_t triId, nonstd::span bounds) @@ -462,29 +468,12 @@ const std::atomic_bool& ComputeVertexToTriangleDistances::getCancel() return m_ShouldCancel; } -// ----------------------------------------------------------------------------- -void ComputeVertexToTriangleDistances::sendThreadSafeProgressMessage(usize counter) -{ - std::lock_guard guard(m_ProgressMessage_Mutex); - - m_ProgressCounter += counter; - auto progressInt = static_cast((static_cast(m_ProgressCounter) / static_cast(m_TotalElements)) * 100.0f); - - if(m_ProgressCounter > 1 && m_LastProgressInt != progressInt) - { - std::string ss = fmt::format("Finding Distances || {}% Completed", progressInt); - m_MessageHandler(IFilter::Message::Type::Info, ss); - } - - m_LastProgressInt = progressInt; -} - // ----------------------------------------------------------------------------- Result<> ComputeVertexToTriangleDistances::operator()() { auto& vertexGeom = m_DataStructure.getDataRefAs(m_InputValues->VertexDataContainer); SharedVertexListT& sourceVertices = vertexGeom.getVertices()->getDataStoreRef(); - m_TotalElements = vertexGeom.getNumberOfVertices(); + usize totalElements = vertexGeom.getNumberOfVertices(); auto& triangleGeom = m_DataStructure.getDataRefAs(m_InputValues->TriangleDataContainer); auto numTris = static_cast(triangleGeom.getNumberOfFaces()); @@ -506,11 +495,16 @@ Result<> ComputeVertexToTriangleDistances::operator()() auto& closestTriangleIdsArray = m_DataStructure.getDataAs(m_InputValues->ClosestTriangleIdArrayPath)->getDataStoreRef(); closestTriangleIdsArray.fill(-1); // -1 means it never found the closest triangle? + MessageHelper messageHelper(m_MessageHandler); + ProgressMessageHelper progressMessageHelper = messageHelper.createProgressMessageHelper(); + progressMessageHelper.setMaxProgresss(totalElements); + progressMessageHelper.setProgressMessageTemplate("Finding Distances || {}% Completed"); + // Allow data-based parallelization ParallelDataAlgorithm dataAlg; dataAlg.setParallelizationEnabled(true); - dataAlg.setRange(0, m_TotalElements); - dataAlg.execute(ComputeVertexToTriangleDistancesImpl(this, triangles, vertices, sourceVertices, distancesArray, closestTriangleIdsArray, normalsArray, m_RTree)); + dataAlg.setRange(0, totalElements); + dataAlg.execute(ComputeVertexToTriangleDistancesImpl(this, triangles, vertices, sourceVertices, distancesArray, closestTriangleIdsArray, normalsArray, m_RTree, progressMessageHelper)); return {}; } diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.hpp index 3001130189..0207b450a5 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.hpp @@ -41,19 +41,11 @@ class SIMPLNXCORE_EXPORT ComputeVertexToTriangleDistances const std::atomic_bool& getCancel(); - void sendThreadSafeProgressMessage(usize counter); - private: DataStructure& m_DataStructure; const ComputeVertexToTriangleDistancesInputValues* m_InputValues = nullptr; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; - - // Thread safe Progress Message - mutable std::mutex m_ProgressMessage_Mutex; - size_t m_TotalElements = 0; - size_t m_ProgressCounter = 0; - size_t m_LastProgressInt = 0; }; } // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/DBSCAN.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/DBSCAN.cpp index 01eb789361..3c07c94412 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/DBSCAN.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/DBSCAN.cpp @@ -6,6 +6,7 @@ #include "simplnx/Utilities/ClusteringUtilities.hpp" #include "simplnx/Utilities/FilterUtilities.hpp" #include "simplnx/Utilities/MaskCompareUtilities.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelDataAlgorithm.hpp" #include @@ -93,7 +94,7 @@ class DBSCANTemplate public: DBSCANTemplate(DBSCAN* filter, const AbstractDataStoreT& inputDataStore, const std::unique_ptr& maskDataArray, AbstractDataStore& fIdsDataStore, - float32 epsilon, int32 minPoints, ClusterUtilities::DistanceMetric distMetric, std::mt19937_64::result_type seed) + float32 epsilon, int32 minPoints, ClusterUtilities::DistanceMetric distMetric, std::mt19937_64::result_type seed, MessageHelper& messageHelper) : m_Filter(filter) , m_InputDataStore(inputDataStore) , m_Mask(maskDataArray) @@ -102,6 +103,7 @@ class DBSCANTemplate , m_MinPoints(minPoints) , m_DistMetric(distMetric) , m_Seed(seed) + , m_MessageHelper(messageHelper) { } ~DBSCANTemplate() = default; @@ -127,19 +129,19 @@ class DBSCANTemplate // In-memory only with current implementation for speed with std::list epsilonNeighborhoods = std::vector>(numTuples); - m_Filter->updateProgress("Finding Neighborhoods in parallel..."); + m_MessageHelper.sendMessage("Finding Neighborhoods in parallel..."); ParallelDataAlgorithm dataAlg; dataAlg.setRange(0ULL, numTuples); dataAlg.execute(FindEpsilonNeighborhoodsImpl(m_Filter, minDist, m_InputDataStore, m_Mask, numCompDims, numTuples, m_DistMetric, epsilonNeighborhoods)); - m_Filter->updateProgress("Neighborhoods found."); + m_MessageHelper.sendMessage("Neighborhoods found."); } std::mt19937_64 gen(m_Seed); std::uniform_int_distribution dist(0, numTuples - 1); - m_Filter->updateProgress("Beginning clustering..."); - auto start = std::chrono::steady_clock::now(); + m_MessageHelper.sendMessage("Beginning clustering..."); + ThrottledMessenger throttledMessenger = m_MessageHelper.createThrottledMessenger(); usize i = 0; uint8 misses = 0; while(std::find(visited.begin(), visited.end(), false) != visited.end()) @@ -192,14 +194,11 @@ class DBSCANTemplate if(m_Mask->isTrue(index)) { visited[index] = true; - auto now = std::chrono::steady_clock::now(); - // Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { + + throttledMessenger.sendThrottledMessage([&]() { float32 progress = (static_cast(index) / static_cast(numTuples)) * 100.0f; - m_Filter->updateProgress(fmt::format("Scanning Data || Visited Point {} of {} || {:.2f}% Completed", index, numTuples, progress)); - start = std::chrono::steady_clock::now(); - } + return fmt::format("Scanning Data || Visited Point {} of {} || {:.2f}% Completed", index, numTuples, progress); + }); std::list neighbors; if constexpr(PrecacheV) @@ -283,7 +282,7 @@ class DBSCANTemplate visited[index] = true; } } - m_Filter->updateProgress("Clustering Complete!"); + m_MessageHelper.sendMessage("Clustering Complete!"); } private: @@ -295,34 +294,39 @@ class DBSCANTemplate int32 m_MinPoints; ClusterUtilities::DistanceMetric m_DistMetric; std::mt19937_64::result_type m_Seed; + MessageHelper& m_MessageHelper; }; struct DBSCANFunctor { template void operator()(bool cache, bool useRandom, DBSCAN* filter, const IDataArray& inputIDataArray, const std::unique_ptr& maskCompare, Int32Array& fIds, - float32 epsilon, int32 minPoints, ClusterUtilities::DistanceMetric distMetric, std::mt19937_64::result_type seed) + float32 epsilon, int32 minPoints, ClusterUtilities::DistanceMetric distMetric, std::mt19937_64::result_type seed, MessageHelper& messageHelper) { if(cache) { if(useRandom) { - DBSCANTemplate(filter, inputIDataArray.template getIDataStoreRefAs>(), maskCompare, fIds.getDataStoreRef(), epsilon, minPoints, distMetric, seed)(); + DBSCANTemplate(filter, inputIDataArray.template getIDataStoreRefAs>(), maskCompare, fIds.getDataStoreRef(), epsilon, minPoints, distMetric, seed, + messageHelper)(); } else { - DBSCANTemplate(filter, inputIDataArray.template getIDataStoreRefAs>(), maskCompare, fIds.getDataStoreRef(), epsilon, minPoints, distMetric, seed)(); + DBSCANTemplate(filter, inputIDataArray.template getIDataStoreRefAs>(), maskCompare, fIds.getDataStoreRef(), epsilon, minPoints, distMetric, seed, + messageHelper)(); } } else { if(useRandom) { - DBSCANTemplate(filter, inputIDataArray.template getIDataStoreRefAs>(), maskCompare, fIds.getDataStoreRef(), epsilon, minPoints, distMetric, seed)(); + DBSCANTemplate(filter, inputIDataArray.template getIDataStoreRefAs>(), maskCompare, fIds.getDataStoreRef(), epsilon, minPoints, distMetric, seed, + messageHelper)(); } else { - DBSCANTemplate(filter, inputIDataArray.template getIDataStoreRefAs>(), maskCompare, fIds.getDataStoreRef(), epsilon, minPoints, distMetric, seed)(); + DBSCANTemplate(filter, inputIDataArray.template getIDataStoreRefAs>(), maskCompare, fIds.getDataStoreRef(), epsilon, minPoints, distMetric, seed, + messageHelper)(); } } } @@ -341,12 +345,6 @@ DBSCAN::DBSCAN(DataStructure& dataStructure, const IFilter::MessageHandler& mesg // ----------------------------------------------------------------------------- DBSCAN::~DBSCAN() noexcept = default; -// ----------------------------------------------------------------------------- -void DBSCAN::updateProgress(const std::string& message) -{ - m_MessageHandler(IFilter::Message::Type::Info, message); -} - // ----------------------------------------------------------------------------- const std::atomic_bool& DBSCAN::getCancel() { @@ -356,6 +354,8 @@ const std::atomic_bool& DBSCAN::getCancel() // ----------------------------------------------------------------------------- Result<> DBSCAN::operator()() { + MessageHelper messageHelper(m_MessageHandler); + auto& clusteringArray = m_DataStructure.getDataRefAs(m_InputValues->ClusteringArrayPath); auto& featureIds = m_DataStructure.getDataRefAs(m_InputValues->FeatureIdsArrayPath); @@ -372,9 +372,9 @@ Result<> DBSCAN::operator()() } ExecuteNeighborFunction(DBSCANFunctor{}, clusteringArray.getDataType(), m_InputValues->AllowCaching, m_InputValues->UseRandom, this, clusteringArray, maskCompare, featureIds, m_InputValues->Epsilon, - m_InputValues->MinPoints, m_InputValues->DistanceMetric, m_InputValues->Seed); + m_InputValues->MinPoints, m_InputValues->DistanceMetric, m_InputValues->Seed, messageHelper); - updateProgress("Resizing Clustering Attribute Matrix..."); + messageHelper.sendMessage("Resizing Clustering Attribute Matrix..."); auto& featureIdsDataStore = featureIds.getDataStoreRef(); int32 maxCluster = *std::max_element(featureIdsDataStore.begin(), featureIdsDataStore.end()); m_DataStructure.getDataAs(m_InputValues->FeatureAM)->resizeTuples(AttributeMatrix::ShapeType{static_cast(maxCluster + 1)}); diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/DBSCAN.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/DBSCAN.hpp index 2dfac37a98..a3681daef1 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/DBSCAN.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/DBSCAN.hpp @@ -44,7 +44,6 @@ class SIMPLNXCORE_EXPORT DBSCAN DBSCAN& operator=(DBSCAN&&) noexcept = delete; Result<> operator()(); - void updateProgress(const std::string& message); const std::atomic_bool& getCancel(); private: diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.cpp index 17ec4ad467..94784888ae 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.cpp @@ -4,6 +4,7 @@ #include "simplnx/DataStructure/DataGroup.hpp" #include "simplnx/DataStructure/Geometry/ImageGeom.hpp" #include "simplnx/Utilities/DataGroupUtilities.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelTaskAlgorithm.hpp" using namespace nx::core; @@ -16,13 +17,14 @@ class ErodeDilateBadDataTransferDataImpl ErodeDilateBadDataTransferDataImpl(const ErodeDilateBadDataTransferDataImpl&) = default; ErodeDilateBadDataTransferDataImpl(ErodeDilateBadData* filterAlg, usize totalPoints, ChoicesParameter::ValueType operation, const Int32AbstractDataStore& featureIds, - const std::vector& neighbors, const std::shared_ptr& dataArrayPtr) + const std::vector& neighbors, const std::shared_ptr& dataArrayPtr, MessageHelper& messageHelper) : m_FilterAlg(filterAlg) , m_TotalPoints(totalPoints) , m_Operation(operation) , m_Neighbors(neighbors) , m_DataArrayPtr(dataArrayPtr) , m_FeatureIds(featureIds) + , m_MessageHelper(messageHelper) { } ErodeDilateBadDataTransferDataImpl(ErodeDilateBadDataTransferDataImpl&&) = default; // Move Constructor Not Implemented @@ -33,17 +35,11 @@ class ErodeDilateBadDataTransferDataImpl void operator()() const { - auto start = std::chrono::steady_clock::now(); + ThrottledMessenger throttledMessenger = m_MessageHelper.createThrottledMessenger(); + std::string arrayName = m_DataArrayPtr->getName(); for(usize i = 0; i < m_TotalPoints; i++) { - auto now = std::chrono::steady_clock::now(); - //// Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - const float progress = static_cast(i) / static_cast(m_TotalPoints) * 100.0f; - m_FilterAlg->updateProgress(fmt::format("Processing {}: {}% completed", m_DataArrayPtr->getName(), static_cast(progress))); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing {}: {}% completed", arrayName, CalculatePercentCompleteAsInt(i, m_TotalPoints)); }); const int32 featureName = m_FeatureIds[i]; const int64 neighbor = m_Neighbors[i]; @@ -64,6 +60,7 @@ class ErodeDilateBadDataTransferDataImpl std::vector m_Neighbors; const std::shared_ptr m_DataArrayPtr; const Int32AbstractDataStore& m_FeatureIds; + MessageHelper& m_MessageHelper; }; } // namespace @@ -85,12 +82,6 @@ const std::atomic_bool& ErodeDilateBadData::getCancel() return m_ShouldCancel; } -// ----------------------------------------------------------------------------- -void ErodeDilateBadData::updateProgress(const std::string& progMessage) -{ - m_MessageHandler({IFilter::Message::Type::Info, progMessage}); -} - // ----------------------------------------------------------------------------- Result<> ErodeDilateBadData::operator()() { @@ -224,6 +215,8 @@ Result<> ErodeDilateBadData::operator()() // Build up a list of the DataArrays that we are going to operate on. const std::vector> voxelArrays = nx::core::GenerateDataArrayList(m_DataStructure, m_InputValues->FeatureIdsArrayPath, m_InputValues->IgnoredDataArrayPaths); + MessageHelper messageHelper(m_MessageHandler); + ParallelTaskAlgorithm taskRunner; taskRunner.setParallelizationEnabled(true); for(const auto& voxelArray : voxelArrays) @@ -235,14 +228,14 @@ Result<> ErodeDilateBadData::operator()() continue; } - taskRunner.execute(ErodeDilateBadDataTransferDataImpl(this, totalPoints, m_InputValues->Operation, featureIds, neighbors, voxelArray)); + taskRunner.execute(ErodeDilateBadDataTransferDataImpl(this, totalPoints, m_InputValues->Operation, featureIds, neighbors, voxelArray, messageHelper)); } taskRunner.wait(); // This will spill over if the number of DataArrays to process does not divide evenly by the number of threads. // Now update the feature Ids auto featureIDataArray = m_DataStructure.getSharedDataAs(m_InputValues->FeatureIdsArrayPath); taskRunner.setParallelizationEnabled(false); // Do this to make the next call synchronous - taskRunner.execute(ErodeDilateBadDataTransferDataImpl(this, totalPoints, m_InputValues->Operation, featureIds, neighbors, featureIDataArray)); + taskRunner.execute(ErodeDilateBadDataTransferDataImpl(this, totalPoints, m_InputValues->Operation, featureIds, neighbors, featureIDataArray, messageHelper)); } return {}; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.hpp index b6cf2d4067..5eb5a734b4 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.hpp @@ -50,7 +50,6 @@ class SIMPLNXCORE_EXPORT ErodeDilateBadData Result<> operator()(); const std::atomic_bool& getCancel(); - void updateProgress(const std::string& progMessage); private: DataStructure& m_DataStructure; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/FeatureFaceCurvature.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/FeatureFaceCurvature.cpp index 9c29efd6a6..2d4b7cd3cc 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/FeatureFaceCurvature.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/FeatureFaceCurvature.cpp @@ -4,6 +4,7 @@ #include "SimplnxCore/Filters/Algorithms/FindNRingNeighbors.hpp" #include "simplnx/DataStructure/DataArray.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelTaskAlgorithm.hpp" #include @@ -89,6 +90,8 @@ Result<> FeatureFaceCurvature::operator()() sharedFeatureFaces[surfaceMeshFeatureFaceIds[t]].push_back(t); } + MessageHelper messageHelper(m_MessageHandler); + /********************************* * We are going to specifically invoke TBB directly instead of using ParallelTaskAlgorithm since we can just queue up all * the tasks while the first tasks start up. TBB will then grab a new task from it's own queue to work on it up to the @@ -98,11 +101,11 @@ Result<> FeatureFaceCurvature::operator()() */ #ifdef SIMPLNX_ENABLE_MULTICORE std::shared_ptr g(new tbb::task_group); - m_MessageHandler(fmt::format("Adding {} Feature Faces to the work queue....", maxFaceId)); -#else - + messageHelper.sendMessage(fmt::format("Adding {} Feature Faces to the work queue....", maxFaceId)); #endif - m_TotalElements = sharedFeatureFaces.size(); + + ProgressMessageHelper progressMessageHelper = messageHelper.createProgressMessageHelper(); + progressMessageHelper.setMaxProgresss(sharedFeatureFaces.size()); for(auto& sharedFeatureFace : sharedFeatureFaces) { @@ -111,14 +114,14 @@ Result<> FeatureFaceCurvature::operator()() CalculateTriangleGroupCurvatures func(this, m_InputValues->NRingCount, triangleIds, m_InputValues->useNormalsForCurveFitting, surfaceMeshPrincipalCurvature1sArrayPtr, surfaceMeshPrincipalCurvature2sArrayPtr, surfaceMeshPrincipalDirection1sArrayPtr, surfaceMeshPrincipalDirection2sArrayPtr, surfaceMeshGaussianCurvaturesArrayPtr, surfaceMeshMeanCurvaturesArrayPtr, surfaceMeshWeingartenMatrixArrayPtr, triangleGeomPtr, surfaceMeshFaceLabelsArrayPtr, - surfaceMeshFaceNormalsArrayPtr, surfaceMeshTriangleCentroidsArrayPtr, m_MessageHandler, m_ShouldCancel); + surfaceMeshFaceNormalsArrayPtr, surfaceMeshTriangleCentroidsArrayPtr, m_MessageHandler, m_ShouldCancel, progressMessageHelper); #ifdef SIMPLNX_ENABLE_MULTICORE { g->run(func); } #else - m_MessageHandler(fmt::format("Working on Face Id {}/{}", std::to_string((sharedFeatureFace).first), std::to_string(maxFaceId))); + messageHelper.sendMessage(fmt::format("Working on Face Id {}/{}", std::to_string((sharedFeatureFace).first), std::to_string(maxFaceId))); { func(); } @@ -126,7 +129,7 @@ Result<> FeatureFaceCurvature::operator()() } #ifdef SIMPLNX_ENABLE_MULTICORE - m_MessageHandler(fmt::format("Waiting on computations to complete.")); + messageHelper.sendMessage("Waiting on computations to complete."); g->wait(); // Wait for all the threads to complete before moving on. #endif @@ -134,24 +137,3 @@ Result<> FeatureFaceCurvature::operator()() triangleGeomPtr->deleteElementsContainingVert(); return {}; } - -// ----------------------------------------------------------------------------- -void FeatureFaceCurvature::sendThreadSafeProgressMessage(usize counter) -{ - std::lock_guard guard(m_ProgressMessage_Mutex); - - m_ProgressCounter += counter; - auto now = std::chrono::steady_clock::now(); - // We DO NOT Want to print at more than once a second. Hey. Don't copy/paste this line. - if(std::chrono::duration_cast(now - m_InitialPoint).count() < 1000) - { - return; - } - - auto progressInt = static_cast((static_cast(m_ProgressCounter) / static_cast(m_TotalElements)) * 100.0f); - std::string ss = fmt::format("{}/{}", m_ProgressCounter, m_TotalElements); - m_MessageHandler(IFilter::Message::Type::Info, ss); - - m_LastProgressInt = progressInt; - m_InitialPoint = std::chrono::steady_clock::now(); -} diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/FeatureFaceCurvature.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/FeatureFaceCurvature.hpp index f0c3f54c10..13259a1213 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/FeatureFaceCurvature.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/FeatureFaceCurvature.hpp @@ -49,20 +49,11 @@ class SIMPLNXCORE_EXPORT FeatureFaceCurvature const std::atomic_bool& getCancel(); - void sendThreadSafeProgressMessage(usize counter); - private: DataStructure& m_DataStructure; const FeatureFaceCurvatureInputValues* m_InputValues = nullptr; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; - - // Thread safe Progress Message - std::chrono::steady_clock::time_point m_InitialPoint = std::chrono::steady_clock::now(); - mutable std::mutex m_ProgressMessage_Mutex; - size_t m_TotalElements = 0; - size_t m_ProgressCounter = 0; - size_t m_LastProgressInt = 0; }; } // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ReadStlFile.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ReadStlFile.cpp index 153aa0c6c3..a206d8eb82 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ReadStlFile.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ReadStlFile.cpp @@ -8,6 +8,7 @@ #include "simplnx/DataStructure/Geometry/TriangleGeom.hpp" #include "simplnx/Utilities/DataArrayUtilities.hpp" #include "simplnx/Utilities/GeometryUtilities.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/StringUtilities.hpp" #include @@ -137,22 +138,14 @@ Result<> ReadStlFile::operator()() uint16_t attr = 0; std::vector triangleAttributeBuffer(std::numeric_limits::max()); // Just allocate a buffer of max UINT16 elements + MessageHelper messageHelper(m_MessageHandler); + ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); + fpos_t pos; - auto start = std::chrono::steady_clock::now(); - int32_t progInt = 0; for(int32_t t = 0; t < triCount; ++t) { - progInt = static_cast(t) / static_cast(triCount) * 100.0f; - - auto now = std::chrono::steady_clock::now(); - // Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - std::string message = fmt::format("Reading {}% Complete", progInt); - m_MessageHandler(nx::core::IFilter::ProgressMessage{nx::core::IFilter::Message::Type::Info, message, progInt}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Reading {}% Complete", CalculatePercentCompleteAsInt(t, triCount)); }); if(m_ShouldCancel) { return {}; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RegularGridSampleSurfaceMesh.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RegularGridSampleSurfaceMesh.cpp index 1a705630c0..d7816860b9 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RegularGridSampleSurfaceMesh.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RegularGridSampleSurfaceMesh.cpp @@ -89,7 +89,6 @@ class SampleSurfaceMeshSliceImpl void operator()() const { - auto start = std::chrono::steady_clock::now(); usize numEdges = m_EdgeGeom.getNumberOfEdges(); std::vector edgeIndices; edgeIndices.reserve(1024); // Reserve some space in the vector. This is just a guess. diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.cpp index 81ccd9e508..317db8a96e 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.cpp @@ -8,14 +8,17 @@ #include "simplnx/DataStructure/Geometry/ImageGeom.hpp" #include "simplnx/Utilities/DataGroupUtilities.hpp" #include "simplnx/Utilities/MaskCompareUtilities.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/ParallelTaskAlgorithm.hpp" using namespace nx::core; namespace { -bool IdentifyNeighbors(ImageGeom& imageGeom, Int32AbstractDataStore& featureIds, std::vector& storageArray, const std::atomic_bool& shouldCancel, RemoveFlaggedFeatures* filter) +bool IdentifyNeighbors(ImageGeom& imageGeom, Int32AbstractDataStore& featureIds, std::vector& storageArray, const std::atomic_bool& shouldCancel, MessageHelper& messageHelper) { + ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); + SizeVec3 uDims = imageGeom.getDimensions(); int64 dims[3] = {static_cast(uDims[0]), static_cast(uDims[1]), static_cast(uDims[2])}; @@ -37,7 +40,7 @@ bool IdentifyNeighbors(ImageGeom& imageGeom, Int32AbstractDataStore& featureIds, if(progressCounter > progressIncrement) { - filter->sendThreadSafeProgressMessage(progressCounter); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing Image... {}%", CalculatePercentCompleteAsInt(k, dims[2])); }); progressCounter = 0; } progressCounter++; @@ -264,25 +267,6 @@ const std::atomic_bool& RemoveFlaggedFeatures::getCancel() return m_ShouldCancel; } -// ----------------------------------------------------------------------------- -void RemoveFlaggedFeatures::sendThreadSafeProgressMessage(size_t counter) -{ - /* This filter is does not currently use multithreading so lock is unnecessary. - * If this fact changes one MUST UNCOMMENT the below line! */ - // std::lock_guard guard(m_ProgressMessage_Mutex); - - m_ProgressCounter += counter; - - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - m_InitialTime).count() > 1000) // every second update - { - auto progressInt = static_cast((static_cast(m_ProgressCounter) / static_cast(m_TotalElements)) * 100.0); - std::string progressMessage = "Processing Image... "; - m_MessageHandler(IFilter::ProgressMessage{IFilter::Message::Type::Progress, progressMessage, static_cast(progressInt)}); - m_InitialTime = std::chrono::steady_clock::now(); - } -} - // ----------------------------------------------------------------------------- Result<> RemoveFlaggedFeatures::operator()() { @@ -307,6 +291,8 @@ Result<> RemoveFlaggedFeatures::operator()() return {}; } + MessageHelper messageHelper(m_MessageHandler); + // Valid values Functionality::Extract and Functionality::ExtractThenRemove if(function != Functionality::Remove) { @@ -408,7 +394,7 @@ Result<> RemoveFlaggedFeatures::operator()() count++; m_MessageHandler(IFilter::ProgressMessage{IFilter::Message::Type::Info, fmt::format("Entering iteration number {}...", count)}); std::fill(neighbors.begin(), neighbors.end(), -1); - shouldLoop = IdentifyNeighbors(imageGeom, featureIds, neighbors, getCancel(), this); + shouldLoop = IdentifyNeighbors(imageGeom, featureIds, neighbors, getCancel(), messageHelper); if(getCancel()) { diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.hpp index 89261c9158..dcaedde7df 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.hpp @@ -45,8 +45,6 @@ class SIMPLNXCORE_EXPORT RemoveFlaggedFeatures Result<> operator()(); - void sendThreadSafeProgressMessage(size_t counter); - const std::atomic_bool& getCancel(); private: @@ -54,12 +52,6 @@ class SIMPLNXCORE_EXPORT RemoveFlaggedFeatures const RemoveFlaggedFeaturesInputValues* m_InputValues = nullptr; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; - - // Threadsafe Progress Message - mutable std::mutex m_ProgressMessage_Mutex; - std::chrono::steady_clock::time_point m_InitialTime = std::chrono::steady_clock::now(); - size_t m_TotalElements = 0; - size_t m_ProgressCounter = 0; }; } // namespace nx::core diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ComputeFeatureNeighborsFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ComputeFeatureNeighborsFilter.cpp index 6ed0cdf3a6..aa962df1df 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ComputeFeatureNeighborsFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ComputeFeatureNeighborsFilter.cpp @@ -13,6 +13,7 @@ #include "simplnx/Parameters/DataGroupSelectionParameter.hpp" #include "simplnx/Parameters/DataObjectNameParameter.hpp" #include "simplnx/Parameters/GeometrySelectionParameter.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/SIMPLConversion.hpp" @@ -244,20 +245,13 @@ Result<> ComputeFeatureNeighborsFilter::executeImpl(DataStructure& dataStructure int32 nListSize = 100; - float progInt = 0.0F; - auto start = std::chrono::steady_clock::now(); + MessageHelper messageHelper(messageHandler); + ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); // Initialize the neighbor lists for(usize i = 1; i < totalFeatures; i++) { auto now = std::chrono::steady_clock::now(); - // Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - progInt = static_cast(i) / static_cast(totalFeatures) * 100.0f; - std::string message = fmt::format("Initializing Neighbor Lists || {:2.0f}% Complete", progInt); - messageHandler(nx::core::IFilter::ProgressMessage{nx::core::IFilter::Message::Type::Info, message, static_cast(progInt)}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Initializing Neighbor Lists || {}% Complete", CalculatePercentCompleteAsInt(i, totalFeatures)); }); if(shouldCancel) { @@ -273,20 +267,10 @@ Result<> ComputeFeatureNeighborsFilter::executeImpl(DataStructure& dataStructure } } - progInt = 0.0F; - start = std::chrono::steady_clock::now(); // Loop over all points to generate the neighbor lists for(usize j = 0; j < totalPoints; j++) { - auto now = std::chrono::steady_clock::now(); - // Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - progInt = static_cast(j) / static_cast(totalPoints) * 100.0f; - std::string message = fmt::format("Determining Neighbor Lists || {:2.0f}% Complete", progInt); - messageHandler(nx::core::IFilter::ProgressMessage{nx::core::IFilter::Message::Type::Info, message, static_cast(progInt)}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Neighbor Lists || {}% Complete", CalculatePercentCompleteAsInt(j, totalPoints)); }); if(shouldCancel) { @@ -359,20 +343,11 @@ Result<> ComputeFeatureNeighborsFilter::executeImpl(DataStructure& dataStructure FloatVec3 spacing = imageGeom.getSpacing(); - progInt = 0; - start = std::chrono::steady_clock::now(); // We do this to create new set of NeighborList objects for(usize i = 1; i < totalFeatures; i++) { - auto now = std::chrono::steady_clock::now(); - // Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - progInt = static_cast(i) / static_cast(totalFeatures) * 100.0f; - std::string message = fmt::format("Calculating Surface Areas || {:2.0f}% Complete", progInt); - messageHandler(nx::core::IFilter::ProgressMessage{nx::core::IFilter::Message::Type::Info, message, static_cast(progInt)}); - start = std::chrono::steady_clock::now(); - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Calculating Surface Areas || {}% Complete", CalculatePercentCompleteAsInt(i, totalFeatures)); }); + if(shouldCancel) { return {}; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/IterativeClosestPointFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/IterativeClosestPointFilter.cpp index 0e7f3e5e7d..d9cb85e54d 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/IterativeClosestPointFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/IterativeClosestPointFilter.cpp @@ -11,6 +11,7 @@ #include "simplnx/Parameters/DataGroupSelectionParameter.hpp" #include "simplnx/Parameters/GeometrySelectionParameter.hpp" #include "simplnx/Parameters/NumberParameter.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/SIMPLConversion.hpp" #include @@ -231,7 +232,8 @@ Result<> IterativeClosestPointFilter::executeImpl(DataStructure& dataStructure, UmeyamaTransform globalTransform; globalTransform << 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1; - auto start = std::chrono::steady_clock::now(); + MessageHelper messageHelper(messageHandler); + ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); for(usize i = 0; i < iters; i++) { if(shouldCancel) @@ -265,12 +267,7 @@ Result<> IterativeClosestPointFilter::executeImpl(DataStructure& dataStructure, // Update the global transform globalTransform = transform * globalTransform; - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - start).count() > 1000) - { - messageHandler(fmt::format("Performing Registration Iterations || {}% Completed", static_cast((static_cast(i) / iters) * 100.0f))); - start = now; - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Performing Registration Iterations || {}% Completed", CalculatePercentCompleteAsInt(i, iters)); }); } auto& transformStore = dataStructure.getDataAs(transformArrayPath)->getDataStoreRef(); diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/MapPointCloudToRegularGridFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/MapPointCloudToRegularGridFilter.cpp index 8225cd1b19..f9a44bd77b 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/MapPointCloudToRegularGridFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/MapPointCloudToRegularGridFilter.cpp @@ -16,9 +16,9 @@ #include "simplnx/Parameters/NumberParameter.hpp" #include "simplnx/Parameters/VectorParameter.hpp" #include "simplnx/Utilities/MaskCompareUtilities.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/Utilities/SIMPLConversion.hpp" -#include #include namespace nx::core @@ -225,7 +225,7 @@ using WarningType = OutOfBoundsType; using ErrorType = OutOfBoundsType; template -Result<> ProcessVertices(const IFilter::MessageHandler& messageHandler, const VertexGeom& vertices, const ImageGeom* image, UInt64AbstractDataStore& voxelIndices, +Result<> ProcessVertices(MessageHelper& messageHelper, const VertexGeom& vertices, const ImageGeom* image, UInt64AbstractDataStore& voxelIndices, const std::unique_ptr& maskCompare, uint64 outOfBoundsValue) { // Validation @@ -239,7 +239,7 @@ Result<> ProcessVertices(const IFilter::MessageHandler& messageHandler, const Ve // Execution usize numVerts = vertices.getNumberOfVertices(); - auto start = std::chrono::steady_clock::now(); + ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); for(int64 i = 0; i < numVerts; i++) { if constexpr(UseMask) @@ -274,12 +274,7 @@ Result<> ProcessVertices(const IFilter::MessageHandler& messageHandler, const Ve count++; } - auto now = std::chrono::steady_clock::now(); - if(std::chrono::duration_cast(now - start).count() > 1000) - { - messageHandler(fmt::format("Computing Point Cloud Voxel Indices || {}% Completed", static_cast((static_cast(i) / numVerts) * 100.0f))); - start = now; - } + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Computing Point Cloud Voxel Indices || {}% Completed", CalculatePercentCompleteAsInt(i, numVerts)); }); } if constexpr(OutOfBoundsType::UsingWarning) @@ -503,37 +498,39 @@ Result<> MapPointCloudToRegularGridFilter::executeImpl(DataStructure& dataStruct auto& voxelIndices = dataStructure.getDataAs(voxelIndicesPath)->getDataStoreRef(); auto outOfBoundsValue = args.value(k_OutOfBoundsValue_Key); + MessageHelper messageHelper(messageHandler); + // Execute the correct ::ProcessVertices, else error out switch(args.value(k_OutOfBoundsHandlingType_Key)) { case k_SilentModeIndex: { if(useMask) { - return ProcessVertices(messageHandler, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); + return ProcessVertices(messageHelper, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); } else { - return ProcessVertices(messageHandler, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); + return ProcessVertices(messageHelper, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); } } case k_WarningModeIndex: { if(useMask) { - return ProcessVertices(messageHandler, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); + return ProcessVertices(messageHelper, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); } else { - return ProcessVertices(messageHandler, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); + return ProcessVertices(messageHelper, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); } } case k_ErrorModeIndex: { if(useMask) { - return ProcessVertices(messageHandler, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); + return ProcessVertices(messageHelper, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); } else { - return ProcessVertices(messageHandler, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); + return ProcessVertices(messageHelper, vertices, image, voxelIndices, maskCompare, outOfBoundsValue); } } default: { diff --git a/src/simplnx/Utilities/AlignSections.cpp b/src/simplnx/Utilities/AlignSections.cpp index 0642f00214..5322454615 100644 --- a/src/simplnx/Utilities/AlignSections.cpp +++ b/src/simplnx/Utilities/AlignSections.cpp @@ -38,20 +38,17 @@ class AlignSectionsTransferDataImpl void operator()() const { + MessageHelper& messageHelper = m_Filter->getMessageHelper(); + + ThrottledMessenger progressMessenger = messageHelper.createThrottledMessenger(); + T var = static_cast(0); - auto start = std::chrono::steady_clock::now(); + std::string arrayName = m_DataArray.getName(); for(size_t i = 1; i < m_Dims[2]; i++) { - auto now = std::chrono::steady_clock::now(); - // Only send updates every 1 second - if(std::chrono::duration_cast(now - start).count() > 1000) - { - std::string message = fmt::format("Processing {}: {}% completed", m_DataArray.getName(), static_cast(100 * (static_cast(i) / static_cast(m_Dims[2])))); - m_Filter->updateProgress(message); - start = std::chrono::steady_clock::now(); - } + progressMessenger.sendThrottledMessage([&]() { return fmt::format("Processing {}: {}% completed", arrayName, CalculatePercentCompleteAsInt(i, m_Dims[2])); }); if(m_Filter->getCancel()) { return; @@ -109,6 +106,7 @@ AlignSections::AlignSections(DataStructure& dataStructure, const std::atomic_boo : m_DataStructure(dataStructure) , m_ShouldCancel(shouldCancel) , m_MessageHandler(mesgHandler) +, m_MessageHelper(mesgHandler) { } @@ -122,9 +120,9 @@ const std::atomic_bool& AlignSections::getCancel() } // ----------------------------------------------------------------------------- -void AlignSections::updateProgress(const std::string& progMessage) +MessageHelper& AlignSections::getMessageHelper() { - m_MessageHandler({IFilter::Message::Type::Info, progMessage}); + return m_MessageHelper; } // ----------------------------------------------------------------------------- @@ -158,7 +156,7 @@ Result<> AlignSections::execute(const SizeVec3& udims, const DataPath& imageGeom return {}; } - m_MessageHandler(fmt::format("Updating DataArray '{}'", cellArrayPath.toString())); + m_MessageHelper.sendMessage(fmt::format("Updating DataArray '{}'", cellArrayPath.toString())); auto& cellArray = m_DataStructure.getDataRefAs(cellArrayPath); ExecuteParallelFunction(cellArray.getDataType(), taskRunner, this, udims, xShifts, yShifts, cellArray); } diff --git a/src/simplnx/Utilities/AlignSections.hpp b/src/simplnx/Utilities/AlignSections.hpp index e166a80c45..2ef906accb 100644 --- a/src/simplnx/Utilities/AlignSections.hpp +++ b/src/simplnx/Utilities/AlignSections.hpp @@ -6,6 +6,7 @@ #include "simplnx/DataStructure/IDataArray.hpp" #include "simplnx/Filter/Arguments.hpp" #include "simplnx/Filter/IFilter.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/simplnx_export.hpp" namespace nx::core @@ -33,7 +34,7 @@ class SIMPLNX_EXPORT AlignSections const std::atomic_bool& getCancel(); - void updateProgress(const std::string& progMessage); + MessageHelper& getMessageHelper(); protected: /** @@ -55,6 +56,7 @@ class SIMPLNX_EXPORT AlignSections DataStructure& m_DataStructure; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; + MessageHelper m_MessageHelper; }; } // namespace nx::core diff --git a/src/simplnx/Utilities/SampleSurfaceMesh.cpp b/src/simplnx/Utilities/SampleSurfaceMesh.cpp index 4b39514477..e0b0f6d458 100644 --- a/src/simplnx/Utilities/SampleSurfaceMesh.cpp +++ b/src/simplnx/Utilities/SampleSurfaceMesh.cpp @@ -86,7 +86,8 @@ class SampleSurfaceMeshImplByPoints { public: SampleSurfaceMeshImplByPoints(SampleSurfaceMesh* filter, const TriangleGeom& faces, const std::vector& faceIds, const std::vector& faceBBs, - const std::vector& points, const usize featureId, Int32AbstractDataStore& polyIds, const std::atomic_bool& shouldCancel) + const std::vector& points, const usize featureId, Int32AbstractDataStore& polyIds, const std::atomic_bool& shouldCancel, + ProgressMessageHelper& progressMessageHelper) : m_Filter(filter) , m_Faces(faces) , m_FaceIds(faceIds) @@ -95,12 +96,15 @@ class SampleSurfaceMeshImplByPoints , m_PolyIds(polyIds) , m_FeatureId(featureId) , m_ShouldCancel(shouldCancel) + , m_ProgressMessageHelper(progressMessageHelper) { } virtual ~SampleSurfaceMeshImplByPoints() = default; void checkPoints(usize start, usize end) const { + ProgressMessenger progressMessenger = m_ProgressMessageHelper.createProgressMessenger(); + usize iter = m_FeatureId; // find bounding box for current feature @@ -125,7 +129,8 @@ class SampleSurfaceMeshImplByPoints // Send some feedback if(pointsVisited % 1000 == 0) { - m_Filter->sendThreadSafeProgressMessage(m_FeatureId, 1000, m_Points.size()); + progressMessenger.sendProgressMessage( + 1000, [&](usize currentProgress, usize maxProgress) { return fmt::format("Feature {} | Points Completed: {} of {}", m_FeatureId, currentProgress, maxProgress); }); } // Check for the filter being cancelled. if(m_ShouldCancel) @@ -149,6 +154,7 @@ class SampleSurfaceMeshImplByPoints Int32AbstractDataStore& m_PolyIds; const usize m_FeatureId = 0; const std::atomic_bool& m_ShouldCancel; + ProgressMessageHelper& m_ProgressMessageHelper; }; } // namespace @@ -157,18 +163,13 @@ SampleSurfaceMesh::SampleSurfaceMesh(DataStructure& dataStructure, const std::at : m_DataStructure(dataStructure) , m_ShouldCancel(shouldCancel) , m_MessageHandler(mesgHandler) +, m_MessageHelper(m_MessageHandler) { } // ----------------------------------------------------------------------------- SampleSurfaceMesh::~SampleSurfaceMesh() noexcept = default; -// ----------------------------------------------------------------------------- -void SampleSurfaceMesh::updateProgress(const std::string& progMessage) -{ - m_MessageHandler({IFilter::Message::Type::Info, progMessage}); -} - // ----------------------------------------------------------------------------- Result<> SampleSurfaceMesh::execute(SampleSurfaceMeshInputValues& inputValues) { @@ -178,7 +179,7 @@ Result<> SampleSurfaceMesh::execute(SampleSurfaceMeshInputValues& inputValues) // pull down faces usize numFaces = faceLabelsSM.getNumberOfTuples(); - updateProgress("Counting number of Features..."); + m_MessageHelper.sendMessage("Counting number of Features..."); // walk through faces to see how many features there are int32 g1 = 0, g2 = 0; @@ -207,7 +208,7 @@ Result<> SampleSurfaceMesh::execute(SampleSurfaceMeshInputValues& inputValues) usize numFeatures = maxFeatureId + 1; std::vector> faceLists(numFeatures); - updateProgress("Counting number of triangle faces per feature ..."); + m_MessageHelper.sendMessage("Counting number of triangle faces per feature ..."); // traverse data to determine number of faces belonging to each feature for(usize i = 0; i < numFaces; i++) @@ -230,7 +231,7 @@ Result<> SampleSurfaceMesh::execute(SampleSurfaceMeshInputValues& inputValues) return {}; } - updateProgress("Allocating triangle faces per feature ..."); + m_MessageHelper.sendMessage("Allocating triangle faces per feature ..."); // fill out lists with number of references to cells std::vector linkLoc(numFaces, 0); @@ -268,7 +269,7 @@ Result<> SampleSurfaceMesh::execute(SampleSurfaceMeshInputValues& inputValues) return {}; } - updateProgress("Vertex Geometry generating sampling points"); + m_MessageHelper.sendMessage("Vertex Geometry generating sampling points"); // generate the list of sampling points from subclass std::vector points = {}; @@ -277,7 +278,10 @@ Result<> SampleSurfaceMesh::execute(SampleSurfaceMeshInputValues& inputValues) // create array to hold which polyhedron (feature) each point falls in auto& polyIds = m_DataStructure.getDataAs(inputValues.FeatureIdsArrayPath)->getDataStoreRef(); - updateProgress("Sampling triangle geometry ..."); + m_MessageHelper.sendMessage("Sampling triangle geometry ..."); + + ProgressMessageHelper progressMessageHelper = m_MessageHelper.createProgressMessageHelper(); + progressMessageHelper.setMaxProgresss(points.size()); // C++11 RIGHT HERE.... auto nthreads = static_cast(std::thread::hardware_concurrency()); // Returns ZERO if not defined on this platform @@ -295,37 +299,11 @@ Result<> SampleSurfaceMesh::execute(SampleSurfaceMeshInputValues& inputValues) { ParallelDataAlgorithm dataAlg; dataAlg.setRange(0, points.size()); - dataAlg.execute(SampleSurfaceMeshImplByPoints(this, triangleGeom, faceLists[featureId], faceBBs, points, featureId, polyIds, m_ShouldCancel)); + dataAlg.execute(SampleSurfaceMeshImplByPoints(this, triangleGeom, faceLists[featureId], faceBBs, points, featureId, polyIds, m_ShouldCancel, progressMessageHelper)); } } - updateProgress("Complete"); + m_MessageHelper.sendMessage("Complete"); return {}; } - -// ----------------------------------------------------------------------------- -void SampleSurfaceMesh::sendThreadSafeProgressMessage(usize featureId, usize numCompleted, usize totalFeatures) -{ - std::lock_guard lock(m_ProgressMessage_Mutex); - - m_ProgressCounter += numCompleted; - auto now = std::chrono::steady_clock::now(); - auto diff = std::chrono::duration_cast(m_InitialTime - now).count(); - if(diff > 1000) - { - std::string progMessage = fmt::format("Feature {} | Points Completed: {} of {}", featureId, m_ProgressCounter, totalFeatures); - float inverseRate = static_cast(diff) / static_cast(m_ProgressCounter - m_LastProgressInt); - auto remainMillis = std::chrono::milliseconds(static_cast(inverseRate * (totalFeatures - m_ProgressCounter))); - auto secs = std::chrono::duration_cast(remainMillis); - remainMillis -= std::chrono::duration_cast(secs); - auto mins = std::chrono::duration_cast(secs); - secs -= std::chrono::duration_cast(mins); - auto hour = std::chrono::duration_cast(mins); - mins -= std::chrono::duration_cast(hour); - progMessage += fmt::format(" || Est. Time Remain: {} hours {} minutes {} seconds", hour.count(), mins.count(), secs.count()); - m_MessageHandler({IFilter::Message::Type::Info, progMessage}); - m_InitialTime = std::chrono::steady_clock::now(); - m_LastProgressInt = m_ProgressCounter; - } -} diff --git a/src/simplnx/Utilities/SampleSurfaceMesh.hpp b/src/simplnx/Utilities/SampleSurfaceMesh.hpp index 2eb2c7d135..d4c05d39cf 100644 --- a/src/simplnx/Utilities/SampleSurfaceMesh.hpp +++ b/src/simplnx/Utilities/SampleSurfaceMesh.hpp @@ -8,6 +8,7 @@ #include "simplnx/Filter/Arguments.hpp" #include "simplnx/Filter/IFilter.hpp" #include "simplnx/Parameters/VectorParameter.hpp" +#include "simplnx/Utilities/MessageHelper.hpp" #include "simplnx/simplnx_export.hpp" namespace nx::core @@ -37,9 +38,6 @@ class SIMPLNX_EXPORT SampleSurfaceMesh */ Result<> execute(SampleSurfaceMeshInputValues& inputValues); - void updateProgress(const std::string& progMessage); - void sendThreadSafeProgressMessage(usize featureId, size_t numCompleted, size_t totalFeatures); - protected: virtual void generatePoints(std::vector& points) = 0; @@ -47,11 +45,6 @@ class SIMPLNX_EXPORT SampleSurfaceMesh DataStructure& m_DataStructure; const std::atomic_bool& m_ShouldCancel; const IFilter::MessageHandler& m_MessageHandler; - - // Thread safe Progress Message - mutable std::mutex m_ProgressMessage_Mutex; - usize m_ProgressCounter = 0; - usize m_LastProgressInt = 0; - std::chrono::steady_clock::time_point m_InitialTime = std::chrono::steady_clock::now(); + MessageHelper m_MessageHelper; }; } // namespace nx::core From e467f5447bc978e421ed48967747f056851c7aeb Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Mon, 23 Jun 2025 17:04:55 -0400 Subject: [PATCH 06/17] Fixed formatting Signed-off-by: Jared Duffey --- .../SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp | 3 ++- .../SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp index fbedd7d735..afbffd5e2f 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp @@ -1283,5 +1283,6 @@ Result<> ComputeArrayStatistics::operator()() const auto* inputArray = m_DataStructure.getDataAs(m_InputValues->SelectedArrayPath); // We must use ExecuteNeighborFunction because the Mode array is a NeighborList - return ExecuteNeighborFunction(ComputeArrayStatisticsByFeatureFunctor{}, inputArray->getDataType(), m_DataStructure, inputArray, arrays, std::make_pair(trueMin, trueMax), m_InputValues, m_ShouldCancel, messageHelper); + return ExecuteNeighborFunction(ComputeArrayStatisticsByFeatureFunctor{}, inputArray->getDataType(), m_DataStructure, inputArray, arrays, std::make_pair(trueMin, trueMax), m_InputValues, + m_ShouldCancel, messageHelper); } diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp index 2ff0b68f0b..7c1f1955f6 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp @@ -13,7 +13,8 @@ namespace class ComputeNeighborhoodsImpl { public: - ComputeNeighborhoodsImpl(ComputeNeighborhoods* filter, usize totalFeatures, const std::vector& bins, const std::vector& criticalDistance, const std::atomic_bool& shouldCancel, ProgressMessageHelper& progressMessageHelper) + ComputeNeighborhoodsImpl(ComputeNeighborhoods* filter, usize totalFeatures, const std::vector& bins, const std::vector& criticalDistance, const std::atomic_bool& shouldCancel, + ProgressMessageHelper& progressMessageHelper) : m_Filter(filter) , m_TotalFeatures(totalFeatures) , m_Bins(bins) From c0518e787f90d1ed4a580ae2b1af48ba3554d417 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Mon, 23 Jun 2025 17:05:04 -0400 Subject: [PATCH 07/17] Fixed missing override Signed-off-by: Jared Duffey --- src/simplnx/Utilities/MessageHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplnx/Utilities/MessageHelper.cpp b/src/simplnx/Utilities/MessageHelper.cpp index 9362d3953a..0ec4e59522 100644 --- a/src/simplnx/Utilities/MessageHelper.cpp +++ b/src/simplnx/Utilities/MessageHelper.cpp @@ -24,7 +24,7 @@ class MessageHandlerSink : public spdlog::sinks::base_sink { } - ~MessageHandlerSink() noexcept = default; + ~MessageHandlerSink() noexcept override = default; MessageHandlerSink(const MessageHandlerSink&) = delete; MessageHandlerSink(MessageHandlerSink&&) = delete; From 586fc0fa6702e4428d3990b0a4ce5f55a0117e09 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Mon, 23 Jun 2025 17:18:34 -0400 Subject: [PATCH 08/17] Fixed incomplete type error on gcc/clang Signed-off-by: Jared Duffey --- src/simplnx/Utilities/MessageHelper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplnx/Utilities/MessageHelper.hpp b/src/simplnx/Utilities/MessageHelper.hpp index e02fa7dbd7..089df33f19 100644 --- a/src/simplnx/Utilities/MessageHelper.hpp +++ b/src/simplnx/Utilities/MessageHelper.hpp @@ -36,7 +36,7 @@ class SIMPLNX_EXPORT Messenger private: struct Impl; - std::unique_ptr m_Impl = nullptr; + std::unique_ptr m_Impl; }; template From 1409bcf0e9452a420e38b00015cd9512e7dcc304 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Tue, 24 Jun 2025 09:14:14 -0400 Subject: [PATCH 09/17] Fixed incorrect parameter Signed-off-by: Jared Duffey --- .../SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp index afbffd5e2f..0b0c0bda66 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayStatistics.cpp @@ -1210,7 +1210,7 @@ Result<> ComputeArrayStatistics::operator()() const auto* inputArray = m_DataStructure.getDataAs(m_InputValues->SelectedArrayPath); // We must use ExecuteNeighborFunction because the Mode array is a NeighborList - return ExecuteNeighborFunction(ComputeArrayStatisticsByFeatureFunctor{}, inputArray->getDataType(), m_DataStructure, inputArray, arrays, numFeatures, m_InputValues, this, messageHelper); + return ExecuteNeighborFunction(ComputeArrayStatisticsByFeatureFunctor{}, inputArray->getDataType(), m_DataStructure, inputArray, arrays, numFeatures, m_InputValues, m_ShouldCancel, messageHelper); } default: { return MakeErrorResult(-506670, fmt::format("Unknown feature id range controls option selected!", trueMin, trueMax)); From 11cba58c15fa194498bf152323b36ab8017223c6 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Tue, 24 Jun 2025 10:11:58 -0400 Subject: [PATCH 10/17] Update src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../Filters/Algorithms/ComputeArrayHistogramByFeature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp index 06dacebad6..118e20fd51 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp @@ -158,7 +158,7 @@ class GenerateFeatureHistogramImpl m_Overflow++; } } // end of numTuples loop - } // end of increment else + } // end of increment else // Bool breaks neighbor lists; if we have made it here we know m_ModalBinRangesList is a nullptr if constexpr(!std::is_same_v) From 1400e8e18ac493f9c30a6292055648daea78bd87 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Wed, 25 Jun 2025 10:13:01 -0400 Subject: [PATCH 11/17] Added doc comments Signed-off-by: Jared Duffey --- src/simplnx/Utilities/MessageHelper.hpp | 155 ++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/src/simplnx/Utilities/MessageHelper.hpp b/src/simplnx/Utilities/MessageHelper.hpp index 089df33f19..15d8cc35db 100644 --- a/src/simplnx/Utilities/MessageHelper.hpp +++ b/src/simplnx/Utilities/MessageHelper.hpp @@ -11,16 +11,33 @@ namespace nx::core { +/** + * @brief Calculates the perecent complete as an int32 + * @param currentProgress + * @param max + * @return + */ inline constexpr int32 CalculatePercentCompleteAsInt(usize currentProgress, usize max) { return static_cast(static_cast(currentProgress) / static_cast(max) * 100.0f); } +/** + * @brief The Messenger class is responsible for the backend for + * processing messages. User code does not need to create this + * class manually. + */ class SIMPLNX_EXPORT Messenger { public: Messenger() = delete; + /** + * @brief Constructs a Messenger using IFilter::MessageHandler + * which must remain valid for the lifetime of this instance. + * User code does not need to call this constructor. + * @param messageHandler + */ Messenger(const IFilter::MessageHandler& messageHandler); ~Messenger() noexcept; @@ -31,7 +48,18 @@ class SIMPLNX_EXPORT Messenger Messenger& operator=(const Messenger&) = delete; Messenger& operator=(Messenger&&) noexcept; + /** + * @brief Sends a message that is guarenteed to be output + * @param message + */ void sendMessage(std::string message); + + /** + * @brief Attempts to send a message but depending on + * how many other messages are being processed it be + * discarded. + * @param message + */ void trySendMessage(std::string message); private: @@ -39,14 +67,27 @@ class SIMPLNX_EXPORT Messenger std::unique_ptr m_Impl; }; +/** + * @brief Functor of the form std::string func() + */ template concept ThrottledMessageFunctor = std::is_invocable_r_v; +/** + * @brief ThrottledMessenger limits the rate at messages are sent + * based on a user provided interval. Works on a per instance basis. + */ class ThrottledMessenger { public: ThrottledMessenger() = delete; + /** + * @brief Constructs a ThrottledMessenger using a Messenger for the actual sending and + * limits the messages to the given interval. User code does not need to call this constructor. + * @param messenger + * @param interval + */ ThrottledMessenger(std::shared_ptr messenger, std::chrono::milliseconds interval) : m_Messenger(std::move(messenger)) , m_LastTime(std::chrono::steady_clock::now()) @@ -54,6 +95,11 @@ class ThrottledMessenger { } + /** + * @brief Checks if a message can be sent and if so calls the given + * functor to construct the message. + * @param functor + */ void sendThrottledMessage(ThrottledMessageFunctor auto functor) { auto now = std::chrono::steady_clock::now(); @@ -65,11 +111,19 @@ class ThrottledMessenger } } + /** + * @brief Returns the last time point at which a message was sent. + * @return + */ std::chrono::steady_clock::time_point getLastTime() const { return m_LastTime; } + /** + * @brief Returns the last time difference between sent messages. + * @return + */ std::chrono::steady_clock::duration getLastTimeDiff() const { return m_LastTimeDiff; @@ -82,9 +136,19 @@ class ThrottledMessenger std::chrono::steady_clock::duration m_LastTimeDiff = {}; }; +/** + * @brief Functor of the form std::string func(usize currentProgress, usize maxProgress) + */ template concept ProgressMessageFunctor = std::is_invocable_r_v; +/** + * @brief ProgressMessageData is the shared data to track total progress between threads. + * Stores the max progress which should be set before sending updates. + * Stores the current progress as an atomic variable to be thread safe. + * Stores an optional message template which will be used to construct messages. + * It should accept two arguments in the fmt format. + */ struct ProgressMessageData { usize m_MaxProgress = 0; @@ -92,22 +156,44 @@ struct ProgressMessageData std::string m_MessageTemplate; }; +/** + * @brief ProgressMessenger can send messages while tracking the total progress among all threads. + * Uses a ThrottledMessenger to actually send the messages. + */ class ProgressMessenger { public: ProgressMessenger() = delete; + /** + * @brief Constructs a ProgressMessenger using the shared ProgressMessageData, Messenger, and interval. + * User code does not need to call this constructor. + * @param progressMessageData + * @param messenger + * @param interval + */ ProgressMessenger(std::shared_ptr progressMessageData, std::shared_ptr messenger, std::chrono::milliseconds interval) : m_ThrottledMessenger(std::move(messenger), interval) , m_ProgressMessageData(std::move(progressMessageData)) { } + /** + * @brief Sends a throttled message. Delegates to ThrottledMessenger + * @param functor + */ void sendThrottledMessage(ThrottledMessageFunctor auto functor) { m_ThrottledMessenger.sendThrottledMessage(functor); } + /** + * @brief Sends a progress message after incrementing by the given amount. + * The functor takes the current progress and max progress as arguments and + * is only called if a message can be sent according to the ThrottledMessenger. + * @param increment + * @param functor + */ void sendProgressMessage(usize increment, ProgressMessageFunctor auto functor) { m_ProgressMessageData->m_CurrentProgress += increment; @@ -115,6 +201,11 @@ class ProgressMessenger sendThrottledMessage(nestedFunctor); } + /** + * @brief Sends a progress message after incrementing by the given amount. + * The message is constructed using the message template. + * @param increment + */ void sendProgressMessage(usize increment) { auto func = [this](usize currentProgress, usize maxProgress) { @@ -125,11 +216,19 @@ class ProgressMessenger sendProgressMessage(increment, func); } + /** + * @brief Returns the shared progress data. + * @return + */ const ProgressMessageData& getProgressMessageData() const { return *m_ProgressMessageData; } + /** + * @brief Returns the underlying ThrottledMessenger. + * @return + */ const ThrottledMessenger& getThrottledMessenger() const { return m_ThrottledMessenger; @@ -140,30 +239,58 @@ class ProgressMessenger std::shared_ptr m_ProgressMessageData = nullptr; }; +/** + * @brief The ProgressMessageHelper class manages the shared progress data + * and is used to create individual ProgressMessenger for each thread. + * Stores a shared Messenger. + */ class ProgressMessageHelper { public: + /** + * @brief Constructs a ProgressMessageHelper using a Messenger. + * User code does not need to call this constructor. + * @param messenger + */ ProgressMessageHelper(std::shared_ptr messenger) : m_Messenger(std::move(messenger)) , m_ProgressMessageData(std::make_shared()) { } + /** + * @brief Creates a ProgressMessenger with the given interval. + * @param interval + * @return + */ ProgressMessenger createProgressMessenger(std::chrono::milliseconds interval = std::chrono::milliseconds(1000)) { return ProgressMessenger(m_ProgressMessageData, m_Messenger, interval); } + /** + * @brief Sets the progress message template. + * E.g. "Completed {}/{}" + * @param messageTemplate + */ void setProgressMessageTemplate(std::string messageTemplate) { m_ProgressMessageData->m_MessageTemplate = std::move(messageTemplate); } + /** + * @brief Sets the maximum progress. + * @param maxProgress + */ void setMaxProgresss(usize maxProgress) { m_ProgressMessageData->m_MaxProgress = maxProgress; } + /** + * @brief Resets the current progress to 0. Should only be called + * when there are no active ProgressMessengers.k + */ void resetProgress() { m_ProgressMessageData->m_CurrentProgress = 0; @@ -174,11 +301,19 @@ class ProgressMessageHelper std::shared_ptr m_ProgressMessageData = nullptr; }; +/** + * @brief The MessageHelper class manages the lifetime of the Messenger class + * for itself and all child classes. Used to create progress and throttled messengers. + */ class MessageHelper { public: MessageHelper() = delete; + /** + * @brief Constructs a MessageHelper using a MessageHandler. + * @param messageHandler + */ MessageHelper(const IFilter::MessageHandler& messageHandler) : m_Messenger(std::make_shared(messageHandler)) { @@ -192,21 +327,41 @@ class MessageHelper MessageHelper& operator=(const MessageHelper&) = delete; MessageHelper& operator=(MessageHelper&&) noexcept = default; + /** + * @brief Sends a message that is guarenteed to be output. + * Delegated to the underlying Messenger. + * @param message + */ void sendMessage(std::string message) { m_Messenger->sendMessage(std::move(message)); } + /** + * @brief Attempts to send a message but depending on + * how many other messages are being processed it be + * discarded. Delegated to the underlying Messenger. + * @param message + */ void trySendMessage(std::string message) { m_Messenger->trySendMessage(std::move(message)); } + /** + * @brief Creates a ThrottledMessenger with the given interval. + * @param interval + * @return + */ ThrottledMessenger createThrottledMessenger(std::chrono::milliseconds interval = std::chrono::milliseconds(1000)) { return ThrottledMessenger(m_Messenger, interval); } + /** + * @brief Creates a ProgresssMessageHelper. + * @return + */ ProgressMessageHelper createProgressMessageHelper() { return ProgressMessageHelper(m_Messenger); From 7c94f65f584650f0c8dceba863dd88f9526804ea Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Thu, 26 Jun 2025 09:48:40 -0400 Subject: [PATCH 12/17] Added ThrottleSink to further reduce message output Signed-off-by: Jared Duffey --- src/simplnx/Utilities/MessageHelper.cpp | 113 +++++++++++++++++++++++- src/simplnx/Utilities/MessageHelper.hpp | 6 +- 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/src/simplnx/Utilities/MessageHelper.cpp b/src/simplnx/Utilities/MessageHelper.cpp index 0ec4e59522..c220c4a2e4 100644 --- a/src/simplnx/Utilities/MessageHelper.cpp +++ b/src/simplnx/Utilities/MessageHelper.cpp @@ -48,25 +48,130 @@ class MessageHandlerSink : public spdlog::sinks::base_sink using MessageHandlerSink_mt = MessageHandlerSink; using MessageHandlerSink_st = MessageHandlerSink; + +template +class ThrottleSink : public spdlog::sinks::base_sink +{ +public: + ThrottleSink() = delete; + + explicit ThrottleSink(BaseDuration rate) + : m_Rate(std::move(rate)) + , m_Sinks() + , m_LastTime(spdlog::log_clock::time_point(BaseDuration::min())) + { + } + + ThrottleSink(BaseDuration rate, std::vector> sinks) + : m_Rate(std::move(rate)) + , m_Sinks(std::move(sinks)) + , m_LastTime(spdlog::log_clock::time_point(BaseDuration::min())) + { + } + + ~ThrottleSink() noexcept override = default; + + ThrottleSink(const ThrottleSink&) = delete; + ThrottleSink(ThrottleSink&&) = delete; + + ThrottleSink& operator=(const ThrottleSink&) = delete; + ThrottleSink& operator=(ThrottleSink&&) = delete; + + void add_sink(std::shared_ptr sink) + { + std::lock_guard lock(BaseSink::mutex_); + m_Sinks.push_back(sink); + } + + void remove_sink(std::shared_ptr sink) + { + std::lock_guard lock(BaseSink::mutex_); + m_Sinks.erase(std::remove(m_Sinks.begin(), m_Sinks.end(), sink), m_Sinks.end()); + } + + void set_sinks(std::vector> sinks) + { + std::lock_guard lock(BaseSink::mutex_); + m_Sinks = std::move(sinks); + } + + std::vector>& sinks() + { + return m_Sinks; + } + +protected: + void sink_it_(const spdlog::details::log_msg& msg) override + { + auto diff = msg.time - m_LastTime; + if(diff >= m_Rate) + { + m_LastTime = msg.time; + } + else + { + return; + } + + for(auto& sink : m_Sinks) + { + if(sink->should_log(msg.level)) + { + sink->log(msg); + } + } + } + + void flush_() override + { + for(auto& sink : m_Sinks) + { + sink->flush(); + } + } + + void set_formatter_(std::unique_ptr sink_formatter) override + { + BaseSink::formatter_ = std::move(sink_formatter); + for(auto& sink : m_Sinks) + { + sink->set_formatter(BaseSink::formatter_->clone()); + } + } + +private: + using BaseSink = spdlog::sinks::base_sink; + + BaseDuration m_Rate; + std::vector> m_Sinks; + spdlog::log_clock::time_point m_LastTime; +}; + +using ThrottleSink_mt = ThrottleSink; +using ThrottleSink_st = ThrottleSink; + } // namespace struct Messenger::Impl { const IFilter::MessageHandler& m_MessageHandler; + std::chrono::milliseconds m_ThrottleRate; std::shared_ptr m_ThrottledLogger = nullptr; std::shared_ptr m_MandatoryLogger = nullptr; Impl() = delete; - Impl(const IFilter::MessageHandler& messageHandler) + Impl(const IFilter::MessageHandler& messageHandler, std::chrono::milliseconds throttleRate) : m_MessageHandler(messageHandler) + , m_ThrottleRate(throttleRate) { auto sink = std::make_shared(m_MessageHandler); + auto throttledSink = std::make_shared(m_ThrottleRate, std::vector>{sink}); spdlog::init_thread_pool(spdlog::details::default_async_q_size, 1U); auto threadPool = spdlog::thread_pool(); m_MandatoryLogger = std::make_shared("MessageHandlerMandatoryLogger", sink, threadPool, spdlog::async_overflow_policy::block); - m_ThrottledLogger = std::make_shared("MessageHandlerThrottledLogger", sink, threadPool, spdlog::async_overflow_policy::overrun_oldest); + m_ThrottledLogger = std::make_shared("MessageHandlerThrottledLogger", throttledSink, threadPool, spdlog::async_overflow_policy::overrun_oldest); } ~Impl() noexcept @@ -91,8 +196,8 @@ struct Messenger::Impl } }; -Messenger::Messenger(const IFilter::MessageHandler& messageHandler) -: m_Impl(std::make_unique(messageHandler)) +Messenger::Messenger(const IFilter::MessageHandler& messageHandler, std::chrono::milliseconds throttleRate) +: m_Impl(std::make_unique(messageHandler, throttleRate)) { } diff --git a/src/simplnx/Utilities/MessageHelper.hpp b/src/simplnx/Utilities/MessageHelper.hpp index 15d8cc35db..241fd2ff2e 100644 --- a/src/simplnx/Utilities/MessageHelper.hpp +++ b/src/simplnx/Utilities/MessageHelper.hpp @@ -38,7 +38,7 @@ class SIMPLNX_EXPORT Messenger * User code does not need to call this constructor. * @param messageHandler */ - Messenger(const IFilter::MessageHandler& messageHandler); + Messenger(const IFilter::MessageHandler& messageHandler, std::chrono::milliseconds throttleRate); ~Messenger() noexcept; @@ -314,8 +314,8 @@ class MessageHelper * @brief Constructs a MessageHelper using a MessageHandler. * @param messageHandler */ - MessageHelper(const IFilter::MessageHandler& messageHandler) - : m_Messenger(std::make_shared(messageHandler)) + MessageHelper(const IFilter::MessageHandler& messageHandler, std::chrono::milliseconds throttleRate = std::chrono::milliseconds(1000)) + : m_Messenger(std::make_shared(messageHandler, throttleRate)) { } From 8dea2a1d31629a430161a4142b6f978083a55211 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Thu, 26 Jun 2025 10:05:02 -0400 Subject: [PATCH 13/17] Switched percent calculation to return float32 * Fixed progress message template doc comment Signed-off-by: Jared Duffey --- .../Algorithms/AlignSectionsMisorientation.cpp | 4 ++-- .../BadDataNeighborOrientationCheck.cpp | 6 +++--- .../ComputeKernelAvgMisorientations.cpp | 7 ++----- .../NeighborOrientationCorrelation.cpp | 4 ++-- .../Algorithms/AlignSectionsFeatureCentroid.cpp | 2 +- .../ComputeArrayHistogramByFeature.cpp | 1 - .../Algorithms/ComputeNeighborListStatistics.cpp | 2 +- .../Filters/Algorithms/ComputeNeighborhoods.cpp | 3 +-- .../ComputeVertexToTriangleDistances.cpp | 2 +- .../Filters/Algorithms/ErodeDilateBadData.cpp | 2 +- .../Filters/Algorithms/ReadStlFile.cpp | 2 +- .../Filters/Algorithms/RemoveFlaggedFeatures.cpp | 2 +- .../Filters/ComputeFeatureNeighborsFilter.cpp | 6 +++--- .../Filters/IterativeClosestPointFilter.cpp | 2 +- .../Filters/MapPointCloudToRegularGridFilter.cpp | 2 +- src/simplnx/Utilities/AlignSections.cpp | 2 +- src/simplnx/Utilities/MessageHelper.hpp | 16 ++++++++++------ 17 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMisorientation.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMisorientation.cpp index cf9d7a72d7..1e92366222 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMisorientation.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/AlignSectionsMisorientation.cpp @@ -92,7 +92,7 @@ Result<> AlignSectionsMisorientation::findShifts(std::vector& xShifts, { return {}; } - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Shifts || {}% Complete", CalculatePercentCompleteAsInt(iter, dims[2])); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Shifts || {:.2f}% Complete", CalculatePercentComplete(iter, dims[2])); }); if(getCancel()) { return {}; @@ -200,7 +200,7 @@ Result<> AlignSectionsMisorientation::findShifts(std::vector& xShifts, // Loop over the Z Direction for(int64_t iter = 1; iter < dims[2]; iter++) { - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Shifts || {}% Complete", CalculatePercentCompleteAsInt(iter, dims[2])); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Shifts || {:.2f}% Complete", CalculatePercentComplete(iter, dims[2])); }); if(getCancel()) { return {}; diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheck.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheck.cpp index 8483957cb9..acc84a781d 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheck.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/BadDataNeighborOrientationCheck.cpp @@ -85,7 +85,7 @@ Result<> BadDataNeighborOrientationCheck::operator()() ThrottledMessenger throttledMessenger = messageHelper.createThrottledMessenger(); for(size_t i = 0; i < totalPoints; i++) { - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing Data '{}'% completed", CalculatePercentCompleteAsInt(i, totalPoints)); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing Data {:.2f}% completed", CalculatePercentComplete(i, totalPoints)); }); if(!maskCompare->isTrue(i)) { @@ -154,8 +154,8 @@ Result<> BadDataNeighborOrientationCheck::operator()() for(size_t i = 0; i < totalPoints; i++) { throttledMessenger.sendThrottledMessage([&]() { - return fmt::format("Level '{}' of '{}' || Processing Data ('{}') '{}'% completed", (startLevel - currentLevel) + 1, startLevel - m_InputValues->NumberOfNeighbors, loopNumber, - CalculatePercentCompleteAsInt(i, totalPoints)); + return fmt::format("Level '{}' of '{}' || Processing Data ('{}') {:.2f}% completed", (startLevel - currentLevel) + 1, startLevel - m_InputValues->NumberOfNeighbors, loopNumber, + CalculatePercentComplete(i, totalPoints)); }); if(neighborCount[i] >= currentLevel && !maskCompare->isTrue(i)) diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.cpp index 9c3d47826b..fcc5ec549f 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/ComputeKernelAvgMisorientations.cpp @@ -70,10 +70,7 @@ class FindKernelAvgMisorientationsImpl if(counter > increment) { - progressMessenger.sendProgressMessage(counter, [&](usize currentProgress, usize maxProgress) { - int32 percentComplete = CalculatePercentCompleteAsInt(currentProgress, maxProgress); - return fmt::format(fmt::runtime(progressMessenger.getProgressMessageData().m_MessageTemplate), percentComplete); - }); + progressMessenger.sendProgressMessage(counter); counter = 0; } @@ -185,7 +182,7 @@ Result<> ComputeKernelAvgMisorientations::operator()() ProgressMessageHelper progressMessageHelper = messageHelper.createProgressMessageHelper(); progressMessageHelper.setMaxProgresss(udims[2] * udims[1] * udims[0]); - progressMessageHelper.setProgressMessageTemplate("Finding Kernel Average Misorientations || {}%"); + progressMessageHelper.setProgressMessageTemplate("Finding Kernel Average Misorientations || {:.2f}%"); typename IParallelAlgorithm::AlgorithmArrays algArrays; algArrays.push_back(m_DataStructure.getDataAs(m_InputValues->CellPhasesArrayPath)); diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.cpp index a41ddafb85..847c8a1639 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/NeighborOrientationCorrelation.cpp @@ -43,7 +43,7 @@ class NeighborOrientationCorrelationTransferDataImpl std::string arrayName = m_DataArrayPtr->getName(); for(size_t i = 0; i < m_TotalPoints; i++) { - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing {}: {}% completed", arrayName, CalculatePercentCompleteAsInt(i, m_TotalPoints)); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing {}: {:.2f}% completed", arrayName, CalculatePercentComplete(i, m_TotalPoints)); }); int64_t neighbor = m_BestNeighbor[i]; if(neighbor != -1) { @@ -134,7 +134,7 @@ Result<> NeighborOrientationCorrelation::operator()() for(size_t i = 0; i < totalPoints; i++) { throttledMessenger.sendThrottledMessage([&]() { - return fmt::format("Level '{}' of '{}' || Processing Data '{}'% completed", (startLevel - currentLevel) + 1, startLevel - m_InputValues->Level, CalculatePercentCompleteAsInt(i, totalPoints)); + return fmt::format("Level '{}' of '{}' || Processing Data {:.2f}% completed", (startLevel - currentLevel) + 1, startLevel - m_InputValues->Level, CalculatePercentComplete(i, totalPoints)); }); if(confidenceIndex[i] < m_InputValues->MinConfidence) diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/AlignSectionsFeatureCentroid.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/AlignSectionsFeatureCentroid.cpp index 93a2cba98d..e6adcc7ba8 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/AlignSectionsFeatureCentroid.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/AlignSectionsFeatureCentroid.cpp @@ -77,7 +77,7 @@ Result<> AlignSectionsFeatureCentroid::findShifts(std::vector& xShifts, { return {}; } - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Shifts || {}% Complete", CalculatePercentCompleteAsInt(iter, dims[2])); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Shifts || {:.2f}% Complete", CalculatePercentComplete(iter, dims[2])); }); size_t count = 0; xCentroid[iter] = 0; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp index 118e20fd51..c26b406625 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp @@ -214,7 +214,6 @@ class GenerateFeatureHistogramImpl if(progressCount > progressIncrement) { progressMessenger.sendProgressMessage(progressCount, [&](usize currentProgress, usize maxProgress) { - int32 percentComplete = CalculatePercentCompleteAsInt(currentProgress, maxProgress); return fmt::format("Calculating feature histograms {}/{}", currentProgress, maxProgress); }); progressCount = 0; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.cpp index b598732d61..1842305800 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborListStatistics.cpp @@ -217,7 +217,7 @@ Result<> ComputeNeighborListStatistics::operator()() MessageHelper messageHelper(m_MessageHandler); ProgressMessageHelper progresssMessageHelper = messageHelper.createProgressMessageHelper(); progresssMessageHelper.setMaxProgresss(numTuples); - progresssMessageHelper.setProgressMessageTemplate("Finding Statistics || {}% Completed"); + progresssMessageHelper.setProgressMessageTemplate("Finding Statistics || {:.2f}% Completed"); // Allow data-based parallelization ParallelDataAlgorithm dataAlg; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp index 7c1f1955f6..f828e709a6 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp @@ -45,7 +45,6 @@ class ComputeNeighborhoodsImpl if(incCount >= increment) { progressMessenger.sendProgressMessage(incCount, [&](usize currentProgress, usize maxProgress) { - int32 percentComplete = CalculatePercentCompleteAsInt(currentProgress, maxProgress); return fmt::format("Calculating feature histograms {}/{}", currentProgress, maxProgress); }); incCount = 0; @@ -144,7 +143,7 @@ Result<> ComputeNeighborhoods::operator()() ProgressMessageHelper progressMessageHelper = m_MessageHelper.createProgressMessageHelper(); progressMessageHelper.setMaxProgresss(totalFeatures); - progressMessageHelper.setProgressMessageTemplate("Finding Feature Neighborhoods: {}"); + progressMessageHelper.setProgressMessageTemplate("Finding Feature Neighborhoods: {:.2f}%"); m_LocalNeighborhoodList.resize(totalFeatures); criticalDistance.resize(totalFeatures); diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.cpp index cfa4feab0e..5801167021 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeVertexToTriangleDistances.cpp @@ -498,7 +498,7 @@ Result<> ComputeVertexToTriangleDistances::operator()() MessageHelper messageHelper(m_MessageHandler); ProgressMessageHelper progressMessageHelper = messageHelper.createProgressMessageHelper(); progressMessageHelper.setMaxProgresss(totalElements); - progressMessageHelper.setProgressMessageTemplate("Finding Distances || {}% Completed"); + progressMessageHelper.setProgressMessageTemplate("Finding Distances || {:.2f}% Completed"); // Allow data-based parallelization ParallelDataAlgorithm dataAlg; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.cpp index 94784888ae..432405234b 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ErodeDilateBadData.cpp @@ -39,7 +39,7 @@ class ErodeDilateBadDataTransferDataImpl std::string arrayName = m_DataArrayPtr->getName(); for(usize i = 0; i < m_TotalPoints; i++) { - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing {}: {}% completed", arrayName, CalculatePercentCompleteAsInt(i, m_TotalPoints)); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing {}: {:.2f}% completed", arrayName, CalculatePercentComplete(i, m_TotalPoints)); }); const int32 featureName = m_FeatureIds[i]; const int64 neighbor = m_Neighbors[i]; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ReadStlFile.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ReadStlFile.cpp index a206d8eb82..bab5791a57 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ReadStlFile.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ReadStlFile.cpp @@ -145,7 +145,7 @@ Result<> ReadStlFile::operator()() for(int32_t t = 0; t < triCount; ++t) { - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Reading {}% Complete", CalculatePercentCompleteAsInt(t, triCount)); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Reading {:.2f}% Complete", CalculatePercentComplete(t, triCount)); }); if(m_ShouldCancel) { return {}; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.cpp index 317db8a96e..cb9be3b34a 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/RemoveFlaggedFeatures.cpp @@ -40,7 +40,7 @@ bool IdentifyNeighbors(ImageGeom& imageGeom, Int32AbstractDataStore& featureIds, if(progressCounter > progressIncrement) { - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing Image... {}%", CalculatePercentCompleteAsInt(k, dims[2])); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Processing Image... {:.2f}%", CalculatePercentComplete(k, dims[2])); }); progressCounter = 0; } progressCounter++; diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ComputeFeatureNeighborsFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ComputeFeatureNeighborsFilter.cpp index aa962df1df..ce55f562de 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ComputeFeatureNeighborsFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/ComputeFeatureNeighborsFilter.cpp @@ -251,7 +251,7 @@ Result<> ComputeFeatureNeighborsFilter::executeImpl(DataStructure& dataStructure for(usize i = 1; i < totalFeatures; i++) { auto now = std::chrono::steady_clock::now(); - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Initializing Neighbor Lists || {}% Complete", CalculatePercentCompleteAsInt(i, totalFeatures)); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Initializing Neighbor Lists || {:.2f}% Complete", CalculatePercentComplete(i, totalFeatures)); }); if(shouldCancel) { @@ -270,7 +270,7 @@ Result<> ComputeFeatureNeighborsFilter::executeImpl(DataStructure& dataStructure // Loop over all points to generate the neighbor lists for(usize j = 0; j < totalPoints; j++) { - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Neighbor Lists || {}% Complete", CalculatePercentCompleteAsInt(j, totalPoints)); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Determining Neighbor Lists || {:.2f}% Complete", CalculatePercentComplete(j, totalPoints)); }); if(shouldCancel) { @@ -346,7 +346,7 @@ Result<> ComputeFeatureNeighborsFilter::executeImpl(DataStructure& dataStructure // We do this to create new set of NeighborList objects for(usize i = 1; i < totalFeatures; i++) { - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Calculating Surface Areas || {}% Complete", CalculatePercentCompleteAsInt(i, totalFeatures)); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Calculating Surface Areas || {:.2f}% Complete", CalculatePercentComplete(i, totalFeatures)); }); if(shouldCancel) { diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/IterativeClosestPointFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/IterativeClosestPointFilter.cpp index d9cb85e54d..a73611b7ad 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/IterativeClosestPointFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/IterativeClosestPointFilter.cpp @@ -267,7 +267,7 @@ Result<> IterativeClosestPointFilter::executeImpl(DataStructure& dataStructure, // Update the global transform globalTransform = transform * globalTransform; - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Performing Registration Iterations || {}% Completed", CalculatePercentCompleteAsInt(i, iters)); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Performing Registration Iterations || {:.2f}% Completed", CalculatePercentComplete(i, iters)); }); } auto& transformStore = dataStructure.getDataAs(transformArrayPath)->getDataStoreRef(); diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/MapPointCloudToRegularGridFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/MapPointCloudToRegularGridFilter.cpp index f9a44bd77b..abbcfce79c 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/MapPointCloudToRegularGridFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/MapPointCloudToRegularGridFilter.cpp @@ -274,7 +274,7 @@ Result<> ProcessVertices(MessageHelper& messageHelper, const VertexGeom& vertice count++; } - throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Computing Point Cloud Voxel Indices || {}% Completed", CalculatePercentCompleteAsInt(i, numVerts)); }); + throttledMessenger.sendThrottledMessage([&]() { return fmt::format("Computing Point Cloud Voxel Indices || {:.2f}% Completed", CalculatePercentComplete(i, numVerts)); }); } if constexpr(OutOfBoundsType::UsingWarning) diff --git a/src/simplnx/Utilities/AlignSections.cpp b/src/simplnx/Utilities/AlignSections.cpp index 5322454615..de5988f7a4 100644 --- a/src/simplnx/Utilities/AlignSections.cpp +++ b/src/simplnx/Utilities/AlignSections.cpp @@ -48,7 +48,7 @@ class AlignSectionsTransferDataImpl for(size_t i = 1; i < m_Dims[2]; i++) { - progressMessenger.sendThrottledMessage([&]() { return fmt::format("Processing {}: {}% completed", arrayName, CalculatePercentCompleteAsInt(i, m_Dims[2])); }); + progressMessenger.sendThrottledMessage([&]() { return fmt::format("Processing {}: {:.2f}% completed", arrayName, CalculatePercentComplete(i, m_Dims[2])); }); if(m_Filter->getCancel()) { return; diff --git a/src/simplnx/Utilities/MessageHelper.hpp b/src/simplnx/Utilities/MessageHelper.hpp index 241fd2ff2e..22398455bc 100644 --- a/src/simplnx/Utilities/MessageHelper.hpp +++ b/src/simplnx/Utilities/MessageHelper.hpp @@ -12,14 +12,16 @@ namespace nx::core { /** - * @brief Calculates the perecent complete as an int32 + * @brief Calculates the perecent complete + * @tparam T * @param currentProgress * @param max * @return */ -inline constexpr int32 CalculatePercentCompleteAsInt(usize currentProgress, usize max) +template +inline constexpr T CalculatePercentComplete(usize currentProgress, usize max) { - return static_cast(static_cast(currentProgress) / static_cast(max) * 100.0f); + return static_cast(static_cast(currentProgress) / static_cast(max) * 100.0f); } /** @@ -147,7 +149,8 @@ concept ProgressMessageFunctor = std::is_invocable_r_vm_MessageTemplate), percentComplete); }; @@ -270,7 +273,8 @@ class ProgressMessageHelper /** * @brief Sets the progress message template. - * E.g. "Completed {}/{}" + * Accepts the percent complete as a float32. + * E.g. "Completed {}%" * @param messageTemplate */ void setProgressMessageTemplate(std::string messageTemplate) From 9f06e21a6a578a5d6044cc5c56601fd076ca24b7 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Thu, 26 Jun 2025 12:14:18 -0400 Subject: [PATCH 14/17] Let thread pool persist for multiple filters Signed-off-by: Jared Duffey --- src/simplnx/Utilities/MessageHelper.cpp | 28 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/simplnx/Utilities/MessageHelper.cpp b/src/simplnx/Utilities/MessageHelper.cpp index c220c4a2e4..3967d055c2 100644 --- a/src/simplnx/Utilities/MessageHelper.cpp +++ b/src/simplnx/Utilities/MessageHelper.cpp @@ -150,6 +150,19 @@ class ThrottleSink : public spdlog::sinks::base_sink using ThrottleSink_mt = ThrottleSink; using ThrottleSink_st = ThrottleSink; +std::shared_ptr GetOrCreateThreadPool() +{ + auto& registry = spdlog::details::registry::instance(); + auto& mutex = registry.tp_mutex(); + std::lock_guard lock(mutex); + auto threadPool = registry.get_tp(); + if(threadPool == nullptr) + { + threadPool = std::make_shared(spdlog::details::default_async_q_size, 1U); + registry.set_tp(threadPool); + } + return threadPool; +} } // namespace struct Messenger::Impl @@ -160,6 +173,9 @@ struct Messenger::Impl std::shared_ptr m_ThrottledLogger = nullptr; std::shared_ptr m_MandatoryLogger = nullptr; + static constexpr StringLiteral k_MandatoryLoggerName = "MessageHandlerMandatoryLogger"; + static constexpr StringLiteral k_ThrottledLoggerName = "MessageHandlerThrottledLogger"; + Impl() = delete; Impl(const IFilter::MessageHandler& messageHandler, std::chrono::milliseconds throttleRate) @@ -168,16 +184,12 @@ struct Messenger::Impl { auto sink = std::make_shared(m_MessageHandler); auto throttledSink = std::make_shared(m_ThrottleRate, std::vector>{sink}); - spdlog::init_thread_pool(spdlog::details::default_async_q_size, 1U); - auto threadPool = spdlog::thread_pool(); - m_MandatoryLogger = std::make_shared("MessageHandlerMandatoryLogger", sink, threadPool, spdlog::async_overflow_policy::block); - m_ThrottledLogger = std::make_shared("MessageHandlerThrottledLogger", throttledSink, threadPool, spdlog::async_overflow_policy::overrun_oldest); + auto threadPool = GetOrCreateThreadPool(); + m_MandatoryLogger = std::make_shared(k_MandatoryLoggerName, sink, threadPool, spdlog::async_overflow_policy::block); + m_ThrottledLogger = std::make_shared(k_ThrottledLoggerName, throttledSink, threadPool, spdlog::async_overflow_policy::overrun_oldest); } - ~Impl() noexcept - { - spdlog::shutdown(); - } + ~Impl() noexcept = default; Impl(const Impl&) = delete; Impl(Impl&&) noexcept = delete; From d77588729a8acee938b2af906db59a41cc88c2b3 Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Thu, 26 Jun 2025 12:23:10 -0400 Subject: [PATCH 15/17] Fixed formatting Signed-off-by: Jared Duffey --- .../Filters/Algorithms/ComputeArrayHistogramByFeature.cpp | 5 ++--- .../SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp index c26b406625..1f4acc0f57 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeArrayHistogramByFeature.cpp @@ -213,9 +213,8 @@ class GenerateFeatureHistogramImpl progressCount++; if(progressCount > progressIncrement) { - progressMessenger.sendProgressMessage(progressCount, [&](usize currentProgress, usize maxProgress) { - return fmt::format("Calculating feature histograms {}/{}", currentProgress, maxProgress); - }); + progressMessenger.sendProgressMessage(progressCount, + [&](usize currentProgress, usize maxProgress) { return fmt::format("Calculating feature histograms {}/{}", currentProgress, maxProgress); }); progressCount = 0; } } diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp index f828e709a6..b2387e11ef 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/ComputeNeighborhoods.cpp @@ -44,9 +44,7 @@ class ComputeNeighborhoodsImpl incCount++; if(incCount >= increment) { - progressMessenger.sendProgressMessage(incCount, [&](usize currentProgress, usize maxProgress) { - return fmt::format("Calculating feature histograms {}/{}", currentProgress, maxProgress); - }); + progressMessenger.sendProgressMessage(incCount, [&](usize currentProgress, usize maxProgress) { return fmt::format("Calculating feature histograms {}/{}", currentProgress, maxProgress); }); incCount = 0; } From 5ba58ee21e72de42d68bf5cac8554027b9d7cc5b Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Thu, 26 Jun 2025 12:32:08 -0400 Subject: [PATCH 16/17] Fixed shadowing on gcc Signed-off-by: Jared Duffey --- src/simplnx/Utilities/MessageHelper.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/simplnx/Utilities/MessageHelper.cpp b/src/simplnx/Utilities/MessageHelper.cpp index 3967d055c2..9c4460cd9b 100644 --- a/src/simplnx/Utilities/MessageHelper.cpp +++ b/src/simplnx/Utilities/MessageHelper.cpp @@ -113,29 +113,29 @@ class ThrottleSink : public spdlog::sinks::base_sink return; } - for(auto& sink : m_Sinks) + for(auto& childSink : m_Sinks) { - if(sink->should_log(msg.level)) + if(childSink->should_log(msg.level)) { - sink->log(msg); + childSink->log(msg); } } } void flush_() override { - for(auto& sink : m_Sinks) + for(auto& childSink : m_Sinks) { - sink->flush(); + childSink->flush(); } } void set_formatter_(std::unique_ptr sink_formatter) override { BaseSink::formatter_ = std::move(sink_formatter); - for(auto& sink : m_Sinks) + for(auto& childSink : m_Sinks) { - sink->set_formatter(BaseSink::formatter_->clone()); + childSink->set_formatter(BaseSink::formatter_->clone()); } } From eddc73a8fb1a5c82d78d1aa386160da65d54cc5b Mon Sep 17 00:00:00 2001 From: Jared Duffey Date: Mon, 30 Jun 2025 11:54:26 -0400 Subject: [PATCH 17/17] Fixed thread pool race condition * Now use thread pool specifically for filter messaging instead of spdlog's global pool Signed-off-by: Jared Duffey --- src/simplnx/Utilities/MessageHelper.cpp | 65 +++++++++++++++++++------ 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/src/simplnx/Utilities/MessageHelper.cpp b/src/simplnx/Utilities/MessageHelper.cpp index 9c4460cd9b..18da7de8e8 100644 --- a/src/simplnx/Utilities/MessageHelper.cpp +++ b/src/simplnx/Utilities/MessageHelper.cpp @@ -150,19 +150,49 @@ class ThrottleSink : public spdlog::sinks::base_sink using ThrottleSink_mt = ThrottleSink; using ThrottleSink_st = ThrottleSink; -std::shared_ptr GetOrCreateThreadPool() +class ThreadPool { - auto& registry = spdlog::details::registry::instance(); - auto& mutex = registry.tp_mutex(); - std::lock_guard lock(mutex); - auto threadPool = registry.get_tp(); - if(threadPool == nullptr) +public: + ThreadPool(const ThreadPool&) = delete; + ThreadPool(ThreadPool&&) = delete; + + ThreadPool& operator=(const ThreadPool&) = delete; + ThreadPool& operator=(ThreadPool&&) = delete; + + static ThreadPool& GetInstance() { - threadPool = std::make_shared(spdlog::details::default_async_q_size, 1U); - registry.set_tp(threadPool); + static ThreadPool threadPool; + return threadPool; } - return threadPool; -} + + std::shared_ptr GetOrCreateThreadPool() + { + std::lock_guard lock(m_Mutex); + + if(m_ThreadPool == nullptr) + { + m_ThreadPool = std::make_shared(spdlog::details::default_async_q_size, 1U); + } + + return m_ThreadPool; + } + + void tryReset() + { + std::lock_guard lock(m_Mutex); + if(m_ThreadPool.use_count() == 1) + { + m_ThreadPool = nullptr; + } + } + +private: + ThreadPool() = default; + ~ThreadPool() noexcept = default; + + std::shared_ptr m_ThreadPool = nullptr; + std::recursive_mutex m_Mutex; +}; } // namespace struct Messenger::Impl @@ -172,6 +202,7 @@ struct Messenger::Impl std::shared_ptr m_ThrottledLogger = nullptr; std::shared_ptr m_MandatoryLogger = nullptr; + std::shared_ptr m_ThreadPool = nullptr; static constexpr StringLiteral k_MandatoryLoggerName = "MessageHandlerMandatoryLogger"; static constexpr StringLiteral k_ThrottledLoggerName = "MessageHandlerThrottledLogger"; @@ -184,12 +215,18 @@ struct Messenger::Impl { auto sink = std::make_shared(m_MessageHandler); auto throttledSink = std::make_shared(m_ThrottleRate, std::vector>{sink}); - auto threadPool = GetOrCreateThreadPool(); - m_MandatoryLogger = std::make_shared(k_MandatoryLoggerName, sink, threadPool, spdlog::async_overflow_policy::block); - m_ThrottledLogger = std::make_shared(k_ThrottledLoggerName, throttledSink, threadPool, spdlog::async_overflow_policy::overrun_oldest); + m_ThreadPool = ThreadPool::GetInstance().GetOrCreateThreadPool(); + m_MandatoryLogger = std::make_shared(k_MandatoryLoggerName, sink, m_ThreadPool, spdlog::async_overflow_policy::block); + m_ThrottledLogger = std::make_shared(k_ThrottledLoggerName, throttledSink, m_ThreadPool, spdlog::async_overflow_policy::overrun_oldest); } - ~Impl() noexcept = default; + ~Impl() noexcept + { + // The thread pool can be shared between filters i.e. one filter calls another. + // If this is the last instance we can safely destruct the thread pool + m_ThreadPool = nullptr; + ThreadPool::GetInstance().tryReset(); + } Impl(const Impl&) = delete; Impl(Impl&&) noexcept = delete;