timer-benchmarks icon indicating copy to clipboard operation
timer-benchmarks copied to clipboard

新的定时器 时间轮 + 4-dray heap

Open ktprime opened this issue 2 years ago • 0 comments

在我的quic 网络库中,单个用户连接存在6-8个左右定时器(重传,延时发包,心跳,idle,ack, blackhole, send_flush ...),统计显示95%以上的操作删除和插入,真正触发非常少(活跃的ack和重传),每秒10w+次基本操作,需要超高性能的定时器毫秒精度设计方案。

上面各种方案或多或少存在应用场景限制,比如时间轮定时器存在如下缺陷 第一:存在空转现象,尤其是定时器不多,触发分布不均匀 第二:无法O(1)取到最小时间触发点 (比如网络库中 epoll 常用最小触发时间作为超时参数) 另外堆和红黑等实现中高频繁插入删除性能比较差 ,内存局部性性能不友好

我结合时间轮和4叉堆两种不同实现方案整合一直新的定时器, 基本能满足各种设计要求, 时间轮第一级(比如 256 毫秒内)用4叉堆实现。堆的大小至于太大,活跃的定时器基本都在堆中, 时间轮每256 ms更新一次堆中数据。


#pragma once

#include "util/heap_timer.h"
#include "util/wheel_timer.h"

// timer queue implemented with hashed hierarchical wheel + 4-dary heap.
// complexity:
//      AddTimer   CancelTimer   PerTick  GetMinTimer
//       O(1)       O(1)          O(1)            O(1)
//

class HWheelTimer : public TimerBase
{
public:
    HWheelTimer(int64_t now_time, uint32_t timer_size);

    timerId Schedule(int64_t deadline_ms, QTask* cb);
    bool Delay(int64_t deadline_ms, timerId timer_id);
    void Reset(int64_t deadline_ms, timerId timer_id, QTask* cb);

    bool Cancel(const timerId timer_id);
    bool Erase(const timerId timer_id);
    int Run(const int64_t now_ms);
    int64_t NearTime();
    int DumpTimer(char* buffer);

private:
    void tick();
    void add_wheel(TimerNode* node);
    bool cascade(int bucket);
    bool erase_node(TimerNode* node);
    bool erase_slot(WheelList& near, const TimerNode* node);

private:
    int64_t min_expire_;
    uint32_t addws_, addn1_, moves_, runs_, delays_;
#ifndef BIN_HEAP
    DaryHeap min_queue_;
#else
    BinHeap min_queue_;
#endif
    WheelList buckets_[WHEEL_BUCKETS][TVN_SIZE];
};


ktprime avatar Aug 10 '23 05:08 ktprime