Libev 使用三种数据结构来存储不同的观察者。
对于按时间排序的观察者, 堆,例如ev_timer
和ev_periodic
。
关联列表,例如ev_io
,ev_signal
,ev_child
等。
数组,例如ev_prepare
,ev_check
,ev_async
等。
毫无疑问,使用堆来存储计时器观察器。但是选择链表和数组的标准是什么?
存储ev_io观察者的数据结构似乎有点复杂。它首先是一个数组,其fd
作为索引,数组中的元素是ev_io watcher
的链接列表。如果使用链表作为元素,则为数组分配空间更方便。是原因吗?
或者仅仅因为ev_io
的插入或删除操作更频繁而且ev_prepare
似乎更稳定?
还是其他任何原因?
答案 0 :(得分:5)
期望是同一个fd只有少数(通常是一个,几乎总是最多两个)io观察者(类似于信号)。将列表链接放入观察器意味着不需要额外的分配,如果每个观察者使用阵列则需要。如果很多I / O观察者在同一个fd上都处于活动状态,那么这种链表方法会更慢。
使用数组是因为插入和删除非常快(观察者存储数组索引)。使用4字节索引还可以减少64位计算机上的内存需求(每个观察者12个字节,而不是双链表的16个字节),并且使用指针数组意味着所有指针在内存中彼此靠近,扫描时提高缓存效率,这对一些观察者来说是一种常见的操作。
缓存效率也是为什么在2堆上使用4堆的原因,以及将时间值复制到堆数据结构的原因,以避免在堆操作中访问观察程序内存。 / p>
儿童观察者实际上使用固定大小的哈希表,并且期望每个哈希桶的子观察者数量很少,因此列表指针成为观察者数据结构的一部分。
答案 1 :(得分:2)
原因可能是在典型情况下,需要用fd查找ev_io。底层库(epoll,select或者其他)将提供具有某些事件的fd。然后Libev会简单地将它用作索引,并且只是迭代需要调用的事件监视器的链接列表。所以它可以快速发射事件。