定時器:4叉堆與2叉堆的效率比較
阿新 • • 發佈:2019-01-06
在學習muduo,看到定時器的實現方式,提到有一下幾種實現方式。
1.優先順序佇列,即二叉堆。
2.4叉堆,libev中使用的模型即是如此,比二叉堆更高效,因為具有更好的區域性性。
3.二叉搜尋樹。
4.使用set,以pair<timerstamp,Timer*>為key,為了區分兩個timer時間相同的情況。
本文探討2叉堆與4叉堆的效率問題。自實現了一個4-heap,與stl自帶priorityqueue進行對比,統計插入5萬個、10萬個、20萬個定時器的時間和全部彈出的時間,在linux環境下進行測試。
原始碼檔案結構如下:
timer.h timer.cpp 自實現簡易定時器
Four_heap.h Four_heap.cpp 自實現4-heap
main.cpp 主程式
timer.h
#pragma once #include <iostream> #include <assert.h> class TimerNode { public: TimerNode( int timeout); ~TimerNode(); size_t getExpTime() const { return expiredTime_; } private: size_t expiredTime_; };
timer.cpp
#include "timer.h"
TimerNode::TimerNode(int timeout)
{
expiredTime_ = timeout;
}
TimerNode::~TimerNode()
{
}
Four_heap.h
#include "timer.h" #include <vector> #include <algorithm> #include <deque> #include <cassert> using namespace std; class Four_heap { public: Four_heap(); ~Four_heap(); bool empty(); TimerNode*top(); void pop(); void push(TimerNode *&Node); private: void fixdown(); void fixup(); int getsonIdx(int &cur,int &time);//獲取子節點中最小值對應的下標。 private: vector<TimerNode*> vec; };
Four_heap.cpp
#include "Four_heap.h"
//#define getsondix 減少函式呼叫開銷
Four_heap::Four_heap()
{
vec.reserve(100);
}
Four_heap::~Four_heap()
{
for (auto i : vec)
delete i;
}
bool Four_heap::empty()
{
return vec.empty();
}
TimerNode * Four_heap::top()
{
assert(!vec.empty());
return vec[0];
}
void Four_heap::pop()
{
swap(vec[0], vec.back());
TimerNode*p = vec.back();
vec.pop_back();
delete p;
if (vec.size() > 1)
fixdown();
}
void Four_heap::push(TimerNode* &Node)
{
vec.push_back(Node);
fixup();
}
#ifdef getsondix
void Four_heap::fixdown()//用於pop操作,交換首位元素,彈出尾部元素,然後fixdown()
{
int curIdx = 0;
TimerNode*key = vec[curIdx];
int key_time = key->getExpTime();
int sIdx = getsonIdx(curIdx, key_time);
while (sIdx != -1)
{
if (vec[sIdx]->getExpTime() < key_time)
{
vec[curIdx] = vec[sIdx];
curIdx = sIdx;
sIdx = getsonIdx(curIdx, key_time);
}
else
break;
}
vec[curIdx] = key;
}
#else
void Four_heap::fixdown()//用於pop操作,交換首位元素,彈出尾部元素,然後fixdown()
{
int curIdx = 0;
TimerNode*key = vec[curIdx];
int key_time = key->getExpTime();
int sIdx = -1;
while ((curIdx<<2) + 1 < vec.size())
{
int mintime = key_time;
for (int i = (curIdx << 2) + 1; i <= (curIdx << 2) + 4 && i < vec.size(); i++)
{
int curtime = vec[i]->getExpTime();
mintime = min(curtime, mintime);
if (curtime == mintime)
sIdx = i;
}
if (sIdx == -1)
break;
vec[curIdx] = vec[sIdx];
curIdx = sIdx;
sIdx = -1;
}
vec[curIdx] = key;
}
#endif
void Four_heap::fixup()
{
int curIdx = vec.size() - 1;
if (curIdx == 0)
return;
int fIdx = (curIdx - 1) / 4;
TimerNode*key = vec[curIdx];
int key_time = key->getExpTime();
while (curIdx)
{
if (vec[fIdx]->getExpTime() > key_time)
{
vec[curIdx] = vec[fIdx];
curIdx = fIdx;
fIdx = (curIdx - 1) / 4;
}
else
break;
}
vec[curIdx] = key;
}
int Four_heap::getsonIdx(int &cur,int &time)
{
int i = 4 * cur + 1, mintime = time, idx = -1;
while (i <= 4*cur + 4 && i < vec.size())
{
int curtime = vec[i]->getExpTime();
mintime = min(curtime, mintime);
if (curtime == mintime)
idx = i;
i++;
}
return idx;
}
main.cpp
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <functional>
#include <string>
#include <assert.h>
#include "Four_heap.h"
#include <sys/time.h>
#include <unistd.h>
using namespace std;
const int COUNT = 100000;
struct TimerCmp
{
bool operator()(TimerNode* &a, TimerNode* &b) const
{
return a->getExpTime() > b->getExpTime();
}
};
long long gettime()
{
struct timeval now;
gettimeofday(&now, NULL);
return (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000));
}
long long heap_inserttime;
void FourHeap_timeCount()
{
Four_heap fh;
long long start = gettime();
for (int i = 0; i < COUNT; i++)
{
TimerNode*p = new TimerNode(i);
fh.push(p);
}
// 以毫秒計
long long end = gettime();
heap_inserttime = end - start;
while (!fh.empty())
{
TimerNode*p = fh.top();
cout << p->getExpTime() << endl;
fh.pop();
}
}
long long priority_inserttime;
void Priority_timeCount()
{
priority_queue<TimerNode*, vector<TimerNode*>, TimerCmp> timerNodeQueue;//儲存定時器的容器-優先順序佇列
long long start = gettime();
for (int i = 0; i < COUNT; i++)
{
TimerNode*p = new TimerNode(i);
timerNodeQueue.push(p);
}
long long end = gettime();
priority_inserttime = end - start;
while (!timerNodeQueue.empty())
{
TimerNode*p = timerNodeQueue.top();
cout << p->getExpTime() << endl;
delete p;
timerNodeQueue.pop();
}
}
long long start1;
long long end1;
long long start2;
long long end2;
void test1()
{
start1 = gettime();
Priority_timeCount();
end1 = gettime();
}
void test2()
{
start2 = gettime();
FourHeap_timeCount();
end2 = gettime();
}
int main()
{
// test1();
test2();
cout <<"優先順序佇列插入時間 "<< priority_inserttime << endl;
cout <<"優先順序佇列插入、彈出時間和"<< end1 - start1 << endl;
cout << "4-heap插入時間 " << heap_inserttime << endl;
cout << "4-heap插入、彈出時間和 " << end2 - start2 << endl;
return 0;
}
對test1()和test2()分別進行測試。
結果如下:
第一個表示插入時間,第二個表示插入時間與彈出時間的總和。
插入5萬個定時器:
發現差異並不明顯。
插入10萬個定時器:
可以看到4叉堆效率高於2叉堆。
插入20萬個定時器:
可以看到4叉堆效率高於2叉堆。