diff options
| author | 2023-09-29 13:37:19 +0200 | |
|---|---|---|
| committer | 2023-09-29 13:37:19 +0200 | |
| commit | 184ee2d890baa82be7d92af4f9c864e6893f3490 (patch) | |
| tree | 34214bc52594a38879cdd29d3ed313c678a37f7f | |
| parent | Merge pull request #11546 from Kelebek1/core_timing_mutex (diff) | |
| parent | core_timing: Attempt to reduce heap sifting (diff) | |
| download | yuzu-184ee2d890baa82be7d92af4f9c864e6893f3490.tar.gz yuzu-184ee2d890baa82be7d92af4f9c864e6893f3490.tar.xz yuzu-184ee2d890baa82be7d92af4f9c864e6893f3490.zip | |
Merge pull request #11493 from merryhime/evt
core_timing: Replace queue with a fibonacci heap
| -rw-r--r-- | src/core/core_timing.cpp | 79 | ||||
| -rw-r--r-- | src/core/core_timing.h | 12 | ||||
| -rw-r--r-- | vcpkg.json | 1 |
3 files changed, 53 insertions, 39 deletions
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index b98a0cb33..e671b270f 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -32,6 +32,7 @@ struct CoreTiming::Event { | |||
| 32 | std::uintptr_t user_data; | 32 | std::uintptr_t user_data; |
| 33 | std::weak_ptr<EventType> type; | 33 | std::weak_ptr<EventType> type; |
| 34 | s64 reschedule_time; | 34 | s64 reschedule_time; |
| 35 | heap_t::handle_type handle{}; | ||
| 35 | 36 | ||
| 36 | // Sort by time, unless the times are the same, in which case sort by | 37 | // Sort by time, unless the times are the same, in which case sort by |
| 37 | // the order added to the queue | 38 | // the order added to the queue |
| @@ -122,9 +123,9 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, | |||
| 122 | std::scoped_lock scope{basic_lock}; | 123 | std::scoped_lock scope{basic_lock}; |
| 123 | const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; | 124 | const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; |
| 124 | 125 | ||
| 125 | event_queue.emplace_back( | 126 | auto h{event_queue.emplace( |
| 126 | Event{next_time.count(), event_fifo_id++, user_data, event_type, 0}); | 127 | Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})}; |
| 127 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 128 | (*h).handle = h; |
| 128 | } | 129 | } |
| 129 | 130 | ||
| 130 | event.Set(); | 131 | event.Set(); |
| @@ -138,10 +139,9 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, | |||
| 138 | std::scoped_lock scope{basic_lock}; | 139 | std::scoped_lock scope{basic_lock}; |
| 139 | const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; | 140 | const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; |
| 140 | 141 | ||
| 141 | event_queue.emplace_back( | 142 | auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, user_data, event_type, |
| 142 | Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()}); | 143 | resched_time.count()})}; |
| 143 | 144 | (*h).handle = h; | |
| 144 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | event.Set(); | 147 | event.Set(); |
| @@ -151,15 +151,17 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, | |||
| 151 | std::uintptr_t user_data, bool wait) { | 151 | std::uintptr_t user_data, bool wait) { |
| 152 | { | 152 | { |
| 153 | std::scoped_lock lk{basic_lock}; | 153 | std::scoped_lock lk{basic_lock}; |
| 154 | const auto itr = | 154 | |
| 155 | std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 155 | std::vector<heap_t::handle_type> to_remove; |
| 156 | return e.type.lock().get() == event_type.get() && e.user_data == user_data; | 156 | for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) { |
| 157 | }); | 157 | const Event& e = *itr; |
| 158 | 158 | if (e.type.lock().get() == event_type.get() && e.user_data == user_data) { | |
| 159 | // Removing random items breaks the invariant so we have to re-establish it. | 159 | to_remove.push_back(itr->handle); |
| 160 | if (itr != event_queue.end()) { | 160 | } |
| 161 | event_queue.erase(itr, event_queue.end()); | 161 | } |
| 162 | std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 162 | |
| 163 | for (auto h : to_remove) { | ||
| 164 | event_queue.erase(h); | ||
| 163 | } | 165 | } |
| 164 | } | 166 | } |
| 165 | 167 | ||
| @@ -200,35 +202,45 @@ std::optional<s64> CoreTiming::Advance() { | |||
| 200 | std::scoped_lock lock{advance_lock, basic_lock}; | 202 | std::scoped_lock lock{advance_lock, basic_lock}; |
| 201 | global_timer = GetGlobalTimeNs().count(); | 203 | global_timer = GetGlobalTimeNs().count(); |
| 202 | 204 | ||
| 203 | while (!event_queue.empty() && event_queue.front().time <= global_timer) { | 205 | while (!event_queue.empty() && event_queue.top().time <= global_timer) { |
| 204 | Event evt = std::move(event_queue.front()); | 206 | const Event& evt = event_queue.top(); |
| 205 | std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||
| 206 | event_queue.pop_back(); | ||
| 207 | 207 | ||
| 208 | if (const auto event_type{evt.type.lock()}) { | 208 | if (const auto event_type{evt.type.lock()}) { |
| 209 | basic_lock.unlock(); | 209 | if (evt.reschedule_time == 0) { |
| 210 | const auto evt_user_data = evt.user_data; | ||
| 211 | const auto evt_time = evt.time; | ||
| 212 | |||
| 213 | event_queue.pop(); | ||
| 214 | |||
| 215 | basic_lock.unlock(); | ||
| 216 | |||
| 217 | event_type->callback( | ||
| 218 | evt_user_data, evt_time, | ||
| 219 | std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time}); | ||
| 220 | |||
| 221 | basic_lock.lock(); | ||
| 222 | } else { | ||
| 223 | basic_lock.unlock(); | ||
| 210 | 224 | ||
| 211 | const auto new_schedule_time{event_type->callback( | 225 | const auto new_schedule_time{event_type->callback( |
| 212 | evt.user_data, evt.time, | 226 | evt.user_data, evt.time, |
| 213 | std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})}; | 227 | std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})}; |
| 214 | 228 | ||
| 215 | basic_lock.lock(); | 229 | basic_lock.lock(); |
| 216 | 230 | ||
| 217 | if (evt.reschedule_time != 0) { | ||
| 218 | const auto next_schedule_time{new_schedule_time.has_value() | 231 | const auto next_schedule_time{new_schedule_time.has_value() |
| 219 | ? new_schedule_time.value().count() | 232 | ? new_schedule_time.value().count() |
| 220 | : evt.reschedule_time}; | 233 | : evt.reschedule_time}; |
| 221 | 234 | ||
| 222 | // If this event was scheduled into a pause, its time now is going to be way behind. | 235 | // If this event was scheduled into a pause, its time now is going to be way |
| 223 | // Re-set this event to continue from the end of the pause. | 236 | // behind. Re-set this event to continue from the end of the pause. |
| 224 | auto next_time{evt.time + next_schedule_time}; | 237 | auto next_time{evt.time + next_schedule_time}; |
| 225 | if (evt.time < pause_end_time) { | 238 | if (evt.time < pause_end_time) { |
| 226 | next_time = pause_end_time + next_schedule_time; | 239 | next_time = pause_end_time + next_schedule_time; |
| 227 | } | 240 | } |
| 228 | 241 | ||
| 229 | event_queue.emplace_back( | 242 | event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data, |
| 230 | Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time}); | 243 | evt.type, next_schedule_time, evt.handle}); |
| 231 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | ||
| 232 | } | 244 | } |
| 233 | } | 245 | } |
| 234 | 246 | ||
| @@ -236,7 +248,7 @@ std::optional<s64> CoreTiming::Advance() { | |||
| 236 | } | 248 | } |
| 237 | 249 | ||
| 238 | if (!event_queue.empty()) { | 250 | if (!event_queue.empty()) { |
| 239 | return event_queue.front().time; | 251 | return event_queue.top().time; |
| 240 | } else { | 252 | } else { |
| 241 | return std::nullopt; | 253 | return std::nullopt; |
| 242 | } | 254 | } |
| @@ -274,7 +286,8 @@ void CoreTiming::ThreadLoop() { | |||
| 274 | #endif | 286 | #endif |
| 275 | } | 287 | } |
| 276 | } else { | 288 | } else { |
| 277 | // Queue is empty, wait until another event is scheduled and signals us to continue. | 289 | // Queue is empty, wait until another event is scheduled and signals us to |
| 290 | // continue. | ||
| 278 | wait_set = true; | 291 | wait_set = true; |
| 279 | event.Wait(); | 292 | event.Wait(); |
| 280 | } | 293 | } |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index c20e906fb..26a8b93a7 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -11,7 +11,8 @@ | |||
| 11 | #include <optional> | 11 | #include <optional> |
| 12 | #include <string> | 12 | #include <string> |
| 13 | #include <thread> | 13 | #include <thread> |
| 14 | #include <vector> | 14 | |
| 15 | #include <boost/heap/fibonacci_heap.hpp> | ||
| 15 | 16 | ||
| 16 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 17 | #include "common/thread.h" | 18 | #include "common/thread.h" |
| @@ -151,11 +152,10 @@ private: | |||
| 151 | s64 timer_resolution_ns; | 152 | s64 timer_resolution_ns; |
| 152 | #endif | 153 | #endif |
| 153 | 154 | ||
| 154 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. | 155 | using heap_t = |
| 155 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and | 156 | boost::heap::fibonacci_heap<CoreTiming::Event, boost::heap::compare<std::greater<>>>; |
| 156 | // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't | 157 | |
| 157 | // accommodated by the standard adaptor class. | 158 | heap_t event_queue; |
| 158 | std::vector<Event> event_queue; | ||
| 159 | u64 event_fifo_id = 0; | 159 | u64 event_fifo_id = 0; |
| 160 | 160 | ||
| 161 | std::shared_ptr<EventType> ev_lost; | 161 | std::shared_ptr<EventType> ev_lost; |
diff --git a/vcpkg.json b/vcpkg.json index 7d9e631a1..203fc9708 100644 --- a/vcpkg.json +++ b/vcpkg.json | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | "boost-context", | 12 | "boost-context", |
| 13 | "boost-crc", | 13 | "boost-crc", |
| 14 | "boost-functional", | 14 | "boost-functional", |
| 15 | "boost-heap", | ||
| 15 | "boost-icl", | 16 | "boost-icl", |
| 16 | "boost-intrusive", | 17 | "boost-intrusive", |
| 17 | "boost-mpl", | 18 | "boost-mpl", |