-
Notifications
You must be signed in to change notification settings - Fork 2
Delayed postprocessing timeout logic #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
3314c53
ad8c6ec
7ad14bb
6bcf425
2209892
deddaa8
7b4e158
6e71a70
665b0be
3bd3e89
ed37033
6148b21
e7df040
fb18b44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -132,6 +132,102 @@ class DataHandlingModel : public DataHandlingConcept | |
| std::function<void(IDT&&)> m_consume_callback; | ||
|
|
||
| protected: | ||
| class PostprocessManager { | ||
| public: | ||
| PostprocessManager( | ||
| LatencyBufferType& latency_buffer_impl, RawDataProcessorType& raw_processor_impl, | ||
| uint64_t processing_delay_ticks, uint64_t post_processing_delay_min_wait, uint64_t post_processing_delay_max_wait) : | ||
| m_latency_buffer_impl{latency_buffer_impl}, | ||
| m_raw_processor_impl{raw_processor_impl}, | ||
| m_processing_delay_ticks{processing_delay_ticks}, | ||
| m_post_processing_delay_min_wait{post_processing_delay_min_wait}, | ||
| m_post_processing_delay_max_wait{post_processing_delay_max_wait}, | ||
| m_first_cycle{true}, | ||
| m_last_post_proc_time{std::chrono::system_clock::now()}, | ||
| m_consecutive_timeouts{0}, | ||
| m_max_wait_in_ticks{post_processing_delay_max_wait * 62500} | ||
| { | ||
| } | ||
|
|
||
| // Deferral of the post processing, to allow elements being reordered in the LB | ||
| // Basically, find data older than a certain timestamp and process all data since the last post-processed element up to that value | ||
| int perform_postprocessing(bool timeout) { | ||
| if (m_latency_buffer_impl.occupancy() == 0) { | ||
| return 0; | ||
| } | ||
|
|
||
| if (m_first_cycle) { | ||
| auto head = m_latency_buffer_impl.front(); | ||
| m_unprocessed_element.set_timestamp(head->get_timestamp()); | ||
| m_first_cycle = false; | ||
| TLOG() << "***** First pass post processing *****"; | ||
| } | ||
|
|
||
| // Get the LB boundaries | ||
| auto tail = m_latency_buffer_impl.back(); | ||
| auto newest_ts = tail->get_timestamp(); | ||
|
|
||
| timestamp_t end_win_ts = 0; | ||
| std::chrono::time_point<std::chrono::system_clock> now; | ||
|
|
||
| if (timeout) { | ||
| ++m_consecutive_timeouts; | ||
| timestamp_t timeout_accumulated = m_consecutive_timeouts * m_max_wait_in_ticks; | ||
|
|
||
| // Cap to prevent end_win_ts from becoming unnecessarily large | ||
| timestamp_t timeout_cap = newest_ts + 1; | ||
| timeout_accumulated = std::min(timeout_accumulated, timeout_cap); | ||
|
|
||
| end_win_ts = newest_ts - m_processing_delay_ticks + timeout_accumulated; | ||
|
|
||
| } else { | ||
| m_consecutive_timeouts = 0; | ||
| now = std::chrono::system_clock::now(); | ||
| auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_last_post_proc_time); | ||
|
|
||
| if (milliseconds.count() > m_post_processing_delay_min_wait) { | ||
| if (newest_ts - m_unprocessed_element.get_timestamp() > m_processing_delay_ticks) { | ||
| end_win_ts = newest_ts - m_processing_delay_ticks; | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, this is just an optimization, but maybe still worth considering:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (so we don't even calculate |
||
| if (end_win_ts == 0) { | ||
| return 0; | ||
| } | ||
|
|
||
| auto start_iter = m_latency_buffer_impl.lower_bound(m_unprocessed_element, false); | ||
| m_unprocessed_element.set_timestamp(end_win_ts); | ||
| auto end_iter = m_latency_buffer_impl.lower_bound(m_unprocessed_element, false); | ||
|
|
||
| if (start_iter == end_iter) { | ||
| TLOG_DEBUG(TLVL_WORK_STEPS) << "Nothing to postprocess"; | ||
| return 0; | ||
| } | ||
|
|
||
| int processed = 0; | ||
| for (auto it = start_iter; it != end_iter; ++it) { | ||
| m_raw_processor_impl.postprocess_item(&(*it)); | ||
| ++processed; | ||
| } | ||
|
|
||
| m_last_post_proc_time = now; | ||
|
|
||
| return processed; | ||
| } | ||
|
|
||
| private: | ||
| LatencyBufferType& m_latency_buffer_impl; | ||
| RawDataProcessorType& m_raw_processor_impl; | ||
| const uint64_t m_processing_delay_ticks; | ||
| const uint64_t m_post_processing_delay_min_wait; | ||
| const uint64_t m_post_processing_delay_max_wait; | ||
| bool m_first_cycle; | ||
| RDT m_unprocessed_element; | ||
| int m_consecutive_timeouts; | ||
| const timestamp_t m_max_wait_in_ticks; | ||
| std::chrono::time_point<std::chrono::system_clock> m_last_post_proc_time; | ||
| }; | ||
|
|
||
| // Perform processing operations on payload | ||
| void process_item(RDT&& payload); | ||
|
|
@@ -163,6 +259,12 @@ class DataHandlingModel : public DataHandlingConcept | |
| return { reinterpret_cast<RDT&>(original) }; | ||
| } | ||
|
|
||
| // Actions postprocess scheduler takes if no data arrives in a configured time | ||
| virtual void invoke_postprocess_schedule_timeout_policy() const | ||
| { | ||
| return; // No-op for this class | ||
| } | ||
|
|
||
| // Operational monitoring | ||
| virtual void generate_opmon_data() override; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| #ifndef DATAHANDLINGLIBS_INCLUDE_DATAHANDLINGLIBS_TESTUTILS_UNITTESTUTILITIES_HPP | ||
| #define DATAHANDLINGLIBS_INCLUDE_DATAHANDLINGLIBS_TESTUTILS_UNITTESTUTILITIES_HPP | ||
|
|
||
| #include "datahandlinglibs/models/DataHandlingModel.hpp" | ||
| #include "datahandlinglibs/models/DefaultRequestHandlerModel.hpp" | ||
| #include "datahandlinglibs/models/TaskRawDataProcessorModel.hpp" | ||
|
|
||
| namespace dunedaq { | ||
| namespace datahandlinglibs { | ||
| namespace unittest { | ||
|
|
||
| template<typename ReadoutType, | ||
| typename RequestHandlerType, | ||
| typename LatencyBufferType, | ||
| typename RawDataProcessorType, | ||
| typename InputDataType = ReadoutType> | ||
| class MockDataHandlingModel | ||
| : public | ||
| DataHandlingModel<ReadoutType, RequestHandlerType, LatencyBufferType, RawDataProcessorType, InputDataType> | ||
| { | ||
| public: | ||
| using Base = | ||
| DataHandlingModel<ReadoutType, RequestHandlerType, LatencyBufferType, RawDataProcessorType, InputDataType>; | ||
| using Base::Base; | ||
| using Base::PostprocessManager; | ||
| }; | ||
|
|
||
| } | ||
| } | ||
| } | ||
|
|
||
| #endif // DATAHANDLINGLIBS_INCLUDE_DATAHANDLINGLIBS_TESTUTILS_UNITTESTUTILITIES_HPP |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| /** | ||
| * @file datahandlinglibs_DataHandlingModel_test.cxx Unit Tests for DataHandlingModel | ||
| * | ||
| * This is part of the DUNE DAQ Application Framework, copyright 2020. | ||
| * Licensing/copyright details are in the COPYING file that you should have | ||
| * received with this code. | ||
| */ | ||
|
|
||
| #define BOOST_TEST_MODULE datahandlinglibs_DataHandlingModel_test // NOLINT | ||
|
|
||
| #include "boost/test/unit_test.hpp" | ||
|
|
||
| #include "datahandlinglibs/testutils/UnitTestUtilities.hpp" | ||
| #include "datahandlinglibs/ReadoutTypes.hpp" | ||
| #include "datahandlinglibs/models/SkipListLatencyBufferModel.hpp" | ||
|
|
||
| BOOST_AUTO_TEST_SUITE(datahandlinglibs_DataHandlingModel_test) | ||
|
|
||
| using namespace dunedaq::datahandlinglibs; | ||
|
|
||
| using ReadoutType = types::DUMMY_FRAME_STRUCT; | ||
|
|
||
| BOOST_AUTO_TEST_CASE(DataHandlingModel_postprocess_schedule_SkipListLatencyBufferModel) | ||
| { | ||
| std::atomic<bool> run_marker = true; | ||
|
|
||
| auto model = unittest::MockDataHandlingModel< | ||
| ReadoutType, | ||
| DefaultRequestHandlerModel<ReadoutType, SkipListLatencyBufferModel<ReadoutType>>, | ||
| SkipListLatencyBufferModel<ReadoutType>, | ||
| TaskRawDataProcessorModel<ReadoutType>>(run_marker); | ||
|
|
||
| auto buffer = std::make_shared<SkipListLatencyBufferModel<ReadoutType>>(); | ||
|
|
||
| for (int i = 1; i < 6; i++) { | ||
| ReadoutType frame; | ||
| frame.timestamp = i * 62500; | ||
| buffer->write(std::move(frame)); | ||
| } | ||
|
|
||
| const bool post_processing_enabled = true; | ||
| auto error_registry = std::make_unique<FrameErrorRegistry>(); | ||
|
|
||
| auto raw_processor = | ||
| std::make_shared<TaskRawDataProcessorModel<ReadoutType>>(error_registry, post_processing_enabled); | ||
|
|
||
| const uint64_t delay_ticks = 4 * 62500; | ||
| const uint64_t delay_min_wait = 1; | ||
| const uint64_t delay_max_wait = 2; | ||
|
|
||
| typename decltype(model)::PostprocessManager manager{ | ||
| *buffer, *raw_processor, delay_ticks, delay_min_wait, delay_max_wait}; | ||
|
|
||
| // First pass | ||
| bool timeout = false; | ||
| int processed_count = manager.perform_postprocessing(timeout); | ||
| BOOST_REQUIRE_EQUAL(processed_count, 0); | ||
|
|
||
| timeout = true; | ||
| processed_count += manager.perform_postprocessing(timeout); | ||
| BOOST_REQUIRE_EQUAL(processed_count, 2); | ||
|
|
||
| processed_count += manager.perform_postprocessing(timeout); | ||
| BOOST_REQUIRE_EQUAL(processed_count, 4); | ||
|
|
||
| processed_count += manager.perform_postprocessing(timeout); | ||
| BOOST_REQUIRE_EQUAL(processed_count, 5); | ||
| } | ||
|
|
||
| BOOST_AUTO_TEST_SUITE_END() |
Uh oh!
There was an error while loading. Please reload this page.