summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Fernando S2023-09-29 13:37:19 +0200
committerGravatar GitHub2023-09-29 13:37:19 +0200
commit184ee2d890baa82be7d92af4f9c864e6893f3490 (patch)
tree34214bc52594a38879cdd29d3ed313c678a37f7f
parentMerge pull request #11546 from Kelebek1/core_timing_mutex (diff)
parentcore_timing: Attempt to reduce heap sifting (diff)
downloadyuzu-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.cpp79
-rw-r--r--src/core/core_timing.h12
-rw-r--r--vcpkg.json1
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",