为什么CFS调度器使用红黑树?
Reason why CFS scheduler using red black tree?
CFS 调度程序根据最小虚拟时间选择下一个进程,并使用红黑树 (rbtree) 有效地获取此值,使用 rbtree 我们将获得最小 O(h),这里 h 是 rbtree 的高度。但是,使用最小堆我们只能在 O(1) 时间内获得最小虚拟时间进程。我只想知道为什么在 CFS 实现中不考虑最小堆,在内核级别使用最小堆有什么困难吗?
原因是:堆是基于数组的,因此需要内核中的连续内存 space。这是因为 Linux 中堆的实现方式。查看文件 lib/prio_heap.c
和 include/linux/prio_heap.h
,您会注意到堆 kmalloc
使用 heap_init
。一旦多程序 space 变得庞大,维护数千个 struct sched_entity
需要大量连续的 space(它 运行 在几页中)。从时间和性能的角度来看,人们更喜欢堆,因为一旦选择了 min vruntime
,hepify 操作就可以在后台 运行,但它的 space 要求会造成瓶颈。
由于 rbtree
很容易获得,内核开发人员没有想到实现基于指针的堆,事实上也不需要。
另一个有趣的点是,考虑到您有一个任务(进程或线程)将状态从可运行更改为阻塞(等待 io 或网络资源),那么您需要从runqueue 复杂度为:
- 红黑树的O(log(n))
- 堆的 O(n)
堆的remove操作慢所以红黑树更好
并且当我们得到最小 vruntime 时,堆操作实际上不是 O(1),O(1) 只有在您引用根节点而不删除它时才会发生。但在 CFS 中,我们需要
- 移除它(需要堆化 O(log(n)))
- 更新vruntime,并将其插入回需要O(log(n))的runqueue
CFS 调度程序根据最小虚拟时间选择下一个进程,并使用红黑树 (rbtree) 有效地获取此值,使用 rbtree 我们将获得最小 O(h),这里 h 是 rbtree 的高度。但是,使用最小堆我们只能在 O(1) 时间内获得最小虚拟时间进程。我只想知道为什么在 CFS 实现中不考虑最小堆,在内核级别使用最小堆有什么困难吗?
原因是:堆是基于数组的,因此需要内核中的连续内存 space。这是因为 Linux 中堆的实现方式。查看文件 lib/prio_heap.c
和 include/linux/prio_heap.h
,您会注意到堆 kmalloc
使用 heap_init
。一旦多程序 space 变得庞大,维护数千个 struct sched_entity
需要大量连续的 space(它 运行 在几页中)。从时间和性能的角度来看,人们更喜欢堆,因为一旦选择了 min vruntime
,hepify 操作就可以在后台 运行,但它的 space 要求会造成瓶颈。
由于 rbtree
很容易获得,内核开发人员没有想到实现基于指针的堆,事实上也不需要。
另一个有趣的点是,考虑到您有一个任务(进程或线程)将状态从可运行更改为阻塞(等待 io 或网络资源),那么您需要从runqueue 复杂度为:
- 红黑树的O(log(n))
- 堆的 O(n)
堆的remove操作慢所以红黑树更好
并且当我们得到最小 vruntime 时,堆操作实际上不是 O(1),O(1) 只有在您引用根节点而不删除它时才会发生。但在 CFS 中,我们需要
- 移除它(需要堆化 O(log(n)))
- 更新vruntime,并将其插入回需要O(log(n))的runqueue