-
Notifications
You must be signed in to change notification settings - Fork 126
feat(axtask): add work-stealing load balancing for SMP #1016
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
base: dev
Are you sure you want to change the base?
Changes from 56 commits
b97948b
4bcde68
70a2167
2769b4e
34f5ba3
754a8aa
732fb36
f2a3678
2f401df
5f6432b
f8783d6
8b55fb1
07326e6
df95e32
3259fda
2840e69
585fba4
bc5c352
73ac5e0
85b54f6
6acc246
cbaf784
c86f162
644c1a3
d2e4e21
fb2e36e
1484008
9e9f145
b664dba
1a1ffa1
22d998e
6206c8a
14bdf34
ae3ed2c
38be233
05183fa
0922b7a
c7219f6
1173159
e3b9b7f
7727677
2e229b7
47c9fb5
bb4b4f0
7c9cfe1
18169f1
4d749da
a0f6f84
844acc9
75416e5
494f6d8
ed38ca4
98211ce
2f3a943
3b553ed
60bf0dc
b081e29
22530ce
20db4db
d68b534
83f48c4
146e1c9
ecf535b
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 |
|---|---|---|
|
|
@@ -54,10 +54,38 @@ impl<T> BaseScheduler for FifoScheduler<T> { | |
| self.ready_queue.pop_front() | ||
| } | ||
|
|
||
| fn pick_next_task_matching( | ||
|
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. [建议] 例如 虽然在 work-stealing 场景下影响有限(大部分任务匹配即返回),但作为公开 trait 方法,重排可能引入调度公平性问题。 建议改用 cursor/索引遍历 + 原位删除,或者在 trait doc 中说明 参考 CFS 实现(BTreeMap::remove 不改变排序),FifoScheduler/RRScheduler 也可以用类似思路:遍历 VecDeque 找到匹配项索引,用
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. [建议] SCHED 语义注意: 经验证当前实现正确保持 FIFO 顺序: |
||
| &mut self, | ||
| predicate: impl Fn(&Self::SchedItem) -> bool, | ||
| ) -> Option<Self::SchedItem> { | ||
|
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. 🔍 [建议]
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. [建议/非阻塞] alloc::vec::Vec 暂存在 no_std 内核中会触发堆分配。虽然 try_steal 非热路径,后续可考虑用 cursor API 原地遍历。当前实现语义正确,不作为合入阻塞条件。 |
||
| let mut skipped = alloc::vec::Vec::new(); | ||
|
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. [建议] 代码逻辑正确:
|
||
| loop { | ||
|
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. [建议 - 非阻塞] 后续可考虑:
|
||
| match self.ready_queue.pop_front() { | ||
| Some(task) if predicate(&task) => { | ||
| for t in skipped.into_iter().rev() { | ||
| self.ready_queue.push_front(t); | ||
| } | ||
|
ZR233 marked this conversation as resolved.
Outdated
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. [建议]
非阻塞,后续可考虑用
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. [建议] 在调度热路径上分配
建议改为 in-place 遍历+删除方案。可以利用
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. [建议] 后续可考虑用 |
||
| return Some(task); | ||
|
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. ⚙️ [建议] 当前实现虽然通过 建议考虑使用 fn pick_next_task_matching(
&mut self,
predicate: impl Fn(&Self::SchedItem) -> bool,
) -> Option<Self::SchedItem> {
let mut cursor = self.ready_queue.cursor_front_mut();
while let Some(task) = cursor.current() {
if predicate(task) {
return cursor.remove_current();
}
cursor.move_next();
}
None
}注意:此建议为非阻塞优化项。当前实现语义正确且在锁内原子完成,仅在 |
||
| } | ||
| Some(task) => skipped.push(task), | ||
| None => { | ||
| for t in skipped.into_iter().rev() { | ||
| self.ready_queue.push_front(t); | ||
| } | ||
|
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.
例如队列
A 和 B 互换了位置。对于 work-stealing 场景影响有限(每次只偷一个,且 gc_task 通过 cpumask 绑定不会被偷),但建议后续改用 in-place cursor 实现,在锁内完成过滤且严格保持队列顺序: fn pick_next_task_matching(&mut self, predicate: impl Fn(&Self::SchedItem) -> bool) -> Option<Self::SchedItem> {
let len = self.ready_queue.len();
for i in 0..len {
if let Some(task) = self.ready_queue.pop_front() {
if predicate(&task) {
return Some(task);
}
self.ready_queue.push_back(task);
}
}
None
}这样只遍历一轮,保持原始顺序,且不需要额外的 Vec 分配。 |
||
| return None; | ||
|
ZR233 marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn put_prev_task(&mut self, prev: Self::SchedItem, _preempt: bool) { | ||
| self.ready_queue.push_back(prev); | ||
| } | ||
|
|
||
| fn is_empty(&self) -> bool { | ||
| self.ready_queue.is_empty() | ||
| } | ||
|
|
||
| fn task_tick(&mut self, _current: &Self::SchedItem) -> bool { | ||
| false // no reschedule | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,17 @@ pub trait BaseScheduler { | |
| /// Returns [`None`] if there is not runnable task. | ||
|
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. 🔍 [建议] |
||
| fn pick_next_task(&mut self) -> Option<Self::SchedItem>; | ||
|
|
||
| /// Picks the next task matching a predicate, removing it from the | ||
| /// scheduler. Returns [`None`] if no matching task exists. | ||
| /// | ||
| /// The predicate check and removal happen atomically within a single | ||
| /// lock acquisition — the task is never left in an "out of queue" | ||
| /// window observable by other CPUs. | ||
| fn pick_next_task_matching( | ||
| &mut self, | ||
| predicate: impl Fn(&Self::SchedItem) -> bool, | ||
| ) -> Option<Self::SchedItem>; | ||
|
|
||
| /// Puts the previous task back to the scheduler. The previous task is | ||
| /// usually placed at the end of the ready queue, making it less likely | ||
| /// to be re-scheduled. | ||
|
|
@@ -58,4 +69,11 @@ pub trait BaseScheduler { | |
|
|
||
| /// set priority for a task | ||
| fn set_priority(&mut self, task: &Self::SchedItem, prio: isize) -> bool; | ||
|
|
||
| /// Returns `true` if the ready queue has no runnable tasks. | ||
| /// | ||
| /// Used as a fast, racy heuristic (without holding the lock) to skip | ||
| /// empty queues before attempting work-stealing — mirrors Linux's | ||
| /// `src_rq->nr_running > 1` check in `idle_balance()`. | ||
| fn is_empty(&self) -> bool; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -102,6 +102,30 @@ impl<T, const S: usize> BaseScheduler for RRScheduler<T, S> { | |
| self.ready_queue.pop_front() | ||
| } | ||
|
|
||
| fn pick_next_task_matching( | ||
| &mut self, | ||
| predicate: impl Fn(&Self::SchedItem) -> bool, | ||
| ) -> Option<Self::SchedItem> { | ||
| let mut skipped = alloc::vec::Vec::new(); | ||
| loop { | ||
| match self.ready_queue.pop_front() { | ||
| Some(task) if predicate(&task) => { | ||
| for t in skipped.into_iter().rev() { | ||
| self.ready_queue.push_front(t); | ||
| } | ||
|
ZR233 marked this conversation as resolved.
Outdated
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. ⚙️ [建议] 同 对 RR 调度器的影响比 FIFO 更大——队列顺序直接关系到时间片轮转语义。虽然当前实现通过 非阻塞优化项,语义正确。 |
||
| return Some(task); | ||
| } | ||
| Some(task) => skipped.push(task), | ||
| None => { | ||
| for t in skipped.into_iter().rev() { | ||
| self.ready_queue.push_front(t); | ||
| } | ||
| return None; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn put_prev_task(&mut self, prev: Self::SchedItem, preempt: bool) { | ||
| if prev.time_slice() > 0 && preempt { | ||
| self.ready_queue.push_front(prev) | ||
|
|
@@ -111,6 +135,10 @@ impl<T, const S: usize> BaseScheduler for RRScheduler<T, S> { | |
| } | ||
| } | ||
|
|
||
| fn is_empty(&self) -> bool { | ||
| self.ready_queue.is_empty() | ||
| } | ||
|
|
||
| fn task_tick(&mut self, current: &Self::SchedItem) -> bool { | ||
| let old_slice = current.time_slice.fetch_sub(1, Ordering::Release); | ||
| old_slice <= 1 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -300,6 +300,25 @@ impl<G: BaseGuard, T: ?Sized> BaseSpinLock<G, T> { | |
| // there's no need to lock the inner mutex. | ||
| unsafe { &mut *self.data.get() } | ||
| } | ||
|
|
||
| /// Returns a shared reference to the inner data without acquiring the | ||
| /// lock. The data may be concurrently modified — the result is stale | ||
| /// the instant this returns. Only use for racy heuristics analoguous | ||
| /// to Linux's `src_rq->nr_running > 1` check. | ||
| /// | ||
| /// This is a shared (`&`) reference rather than `&mut`, so it does not | ||
| /// carry `noalias` — the compiler will not assume exclusivity and will | ||
|
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. [建议 - 非阻塞] 建议在 SAFETY 注释中更明确地说明:
|
||
| /// not miscompile concurrent accesses to the same data from other CPUs. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// The caller must ensure that no other thread is concurrently writing | ||
| /// to the inner data in a way that would cause a data race for the | ||
| /// specific fields being read. | ||
| #[inline(always)] | ||
| pub unsafe fn data_unchecked(&self) -> &T { | ||
| unsafe { &*self.data.get() } | ||
| } | ||
| } | ||
|
|
||
| impl<G: BaseGuard, T: Default> Default for BaseSpinLock<G, T> { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.