环形缓冲区,魔戒lordrings,boost的circular_buffer
2月要过去,这个月几乎一点东西没有写,不想懒惰一个月,但有没有什么特别值得写的东西。所以翻了翻存货,抖抖尘土,找了这篇出来。
这个文章Linker看见标题会笑的,呵呵,因为他在2007年就写过一篇类似的文章《环形缓冲区的实现》。但其实我的作品应该早于他的,我的当时作品名字是cycdeuque.Linker当时刺激我的不止他的文章,还有他的类名字,ring。比我的酷了很多。于是把我的实现名字改成了魔戒lordrings。感觉爽了一点,后来稍微浏览了Boost,发现Boost也有类似的实现circular_buffer。
其实也许就是英雄所见略同。
给大家解释一下为啥要有类似的实现,写服务器的代码时,为了减少内存的分配,我们很多时候需要一个池子,讲需要分配的资源先new出来,放在池子里面。这个池子的总长度(容量)应该是大部分时候是固定,
表面看来,这种要求用std::list实现最简单,list可以在开始和结尾的地方增加删除。但你会发现,如果使用std::list作为池子,性能会是你很头疼的一个东西,std::list分配一个加入一个对象,或者释放一个对象,可能都会对应一次new,delete操作。如果这样的操作频繁,std::list就会成为效率的瓶颈。我的一个通讯程序,接受和发送的buffer都考虑用一个池子,而每一个链接的发送数据,也会考虑放在一个池子里面。开始我就是用的list,后来用Oprofile,测试发现了这个问题。我就考虑要换容器实现。于是就有了这个lordrings。后来发现服务器所有需要池子的地方,都可以用这个魔戒解决问题。
看了一下Boost的设计实现circular_buffer提供的接口,以及代码。circular_buffer和我的设计思路几乎一致。使用一个内存数据区作为数据存放区,可以在开始位置和结束位置进行插入和删除操作,开始和,结束位置的指针可以变化,通过模除保证队列可以循环;长度可以扩展;如果队列已经满了,提供插入的时候提供是否覆盖的选择。
图1 BOOST的circular_buffer的设计实现图
不同的地方大约有2点,第一点是circular_buffer的实现完全符合stl的规范,包括分配器,和迭代器等,而我的实现省去了这些东西,我在内部实现了头部尾部的增加,删除,也实现了任意位置的[]方法。第二点是我的lordrings使用了一个小技巧记录区分数据区是空还是满,就是增加一个数据的空间,当记录开始位置的指针等于结束位置的指针的时候表示空,当开始位置和结束位置相差1的时候表示满。而BOOST的circular_buffer增加了一个记录尺寸的变量。现在默默感觉了一下,BOOST的这个方法比较我的要好一点,估计我当时写的时候被自己的内存PIPE设计影响了。
最后把代码贴出来,写的时候在听张楚的姐姐,当时对模板的感觉已经开始入门了。大致进入了侯捷所说的第3个阶段吧。哈哈。
/******************************************************************************************
Copyright : 2000-2004,FXL.
FileName :
Author : Sail(ZENGXING)//Author name here
Version :
Date Of Creation : 2006年3月11日
Description : 一个循环的DEQUE,可以控制在数据满的情况下,是否覆盖
Others : 原来取名cyc_deque,但是一天和Linker聊天他说说他写的类似的类名字是rings,我觉得这个
名字比较酷,所以选择这个名字,越来越觉得这个类好用。
Function List :
1. ......
Modification History:
1.Date :
Author :
Modification :
喔姐姐,我想回家,牵着我的手,我有些困了
******************************************************************************************/
#ifndef _ZEN_EXTEND_STD_CYC_DEQUE_H_
#define _ZEN_EXTEND_STD_CYC_DEQUE_H_
namespace zenlib
{
template<class _value_type >
class lordrings
{
protected:
//循环队列的起始位置
size_t cycdeque_start_;
//循环队列的结束位置,注意我使用的是前开后闭
size_t cycdeque_end_;
//队列的长度,使队列的容量+1
size_t cycdeque_len_;
//存放数据的指针
_value_type *value_ptr_;
public:
//构造函数,后面必须调用,initialize
lordrings():
cycdeque_start_(0),
cycdeque_end_(cycdeque_start_),
cycdeque_len_(1),
value_ptr_(NULL)
{
}
//构造函数,后面完全没有必要调用,initialize
//因为要形成一个前闭后开的空间,所以cycdeque_len_比实际要求的数据长度+1
lordrings(size_t data_len):
cycdeque_start_(0),
cycdeque_end_(cycdeque_start_),
cycdeque_len_(data_len+1),
value_ptr_(NULL)
{
assert(data_len > 0);
value_ptr_ = new _value_type[cycdeque_len_];
}
~lordrings()
{
if(value_ptr_)
{
delete[] value_ptr_;
value_ptr_ = NULL;
}
}
//因为要形成一个前闭后开的空间,所以cycdeque_len_比实际要求的数据长度+1
void initialize(size_t data_len)
{
assert(data_len > 0);
cycdeque_start_ =0;
cycdeque_end_ = cycdeque_start_;
cycdeque_len_ = data_len+1;
//清理现场
if(value_ptr_)
{
delete[] value_ptr_;
value_ptr_ = NULL;
}
value_ptr_ = new _value_type[cycdeque_len_];
}
//
void finit()
{
cycdeque_start_ = 0;
cycdeque_end_ = cycdeque_start_;
cycdeque_len_ = 1;
//清理现场
if(value_ptr_)
{
delete[] value_ptr_;
value_ptr_ = NULL;
}
}
//重新
void clear()
{
cycdeque_start_ =0;
cycdeque_end_ = cycdeque_start_;
}
//尺寸空间
inline size_t size() const
{
//
if ( cycdeque_end_ >= cycdeque_start_ )
{
return cycdeque_end_ - cycdeque_start_ ;
}
else
{
return cycdeque_end_+cycdeque_len_ -cycdeque_start_ ;
}
}
//返回空闲空间的大小
inline size_t freesize() const
{
return cycdeque_len_ -size() -1;
}
//返回队列的容量
inline size_t capacity() const
{
return cycdeque_len_ -1;
}
//检查是否已经满了
inline bool full() const
{
//如果结束+1%
if((cycdeque_end_ + 1)%cycdeque_len_ ==cycdeque_start_)
{
return true;
}
return false;
}
//判断队列是否为空
inline bool empty() const
{
//如果发现开始==结束
if(cycdeque_start_ == cycdeque_end_)
{
return true;
}
return false;
}
//重新分配一个空间,
bool resize(size_t new_size)
{
assert(new_size > 0);
size_t deque_size = size();
//如果原来的尺寸大于新的尺寸,无法扩展
if( deque_size > new_size )
{
return false;
}
_value_type *new_value_ptr = new _value_type[new_size+1];
//调整几个内部参数
cycdeque_start_ =0;
cycdeque_end_ = deque_size;
cycdeque_len_ = new_size+1;
//如果原来有数据
if(value_ptr_ != NULL)
{
for (size_t i=0;i<deque_size;++i)
{
new_value_ptr[i] = value_ptr_[(cycdeque_start_+i)%cycdeque_len_];
}
delete[] value_ptr_;
value_ptr_ = NULL;
}
value_ptr_ = new_value_ptr;
return true;
}
//将一个数据放入队列的尾部,如果队列已经满了,你可以将lay_over参数置位true,覆盖原有的数据
bool push_back(const _value_type &value_data,bool lay_over =false)
{
//
if((cycdeque_end_ + 1)%cycdeque_len_ ==cycdeque_start_ )
{
//如果不要覆盖,返回错误
if(lay_over == false)
{
return false;
}
//如果要覆盖
else
{
//将最后一个位置覆盖,并且调整起始和结束位置
value_ptr_[cycdeque_end_] = value_data;
cycdeque_start_ = (cycdeque_start_ +1 ) % cycdeque_len_;
cycdeque_end_ = (cycdeque_end_+1) % cycdeque_len_;
return true;
}
}
//
value_ptr_[cycdeque_end_] = value_data;
cycdeque_end_ = (cycdeque_end_+1) % cycdeque_len_;
return true;
}
//从队列的前面得到一个数据
bool pop_front(_value_type &value_data)
{
//
if (size() == 0)
{
return false;
}
value_data = value_ptr_[cycdeque_start_];
cycdeque_start_ = (cycdeque_start_ +1 ) % cycdeque_len_;
return true;
}
bool pop_front()
{
//
if (size() == 0)
{
return false;
}
cycdeque_start_ = (cycdeque_start_ +1 ) % cycdeque_len_;
return true;
}
//从队列的前面得到一个数据
bool pop_end(_value_type &value_data)
{
//
if (size() == 0)
{
return false;
}
cycdeque_end_ = (cycdeque_end_ > 0)?cycdeque_end_-1:cycdeque_len_-1;
value_data = value_ptr_[cycdeque_end_];
return true;
}
bool pop_end()
{
//
if (size() == 0)
{
return false;
}
cycdeque_end_ = (cycdeque_end_ > 0)?cycdeque_end_-1:cycdeque_len_-1;
return true;
}
//ID不要越界,自己保证,我没兴趣为你干什么
_value_type& operator[](size_t id)
{
return value_ptr_[(cycdeque_start_ + id) % cycdeque_len_];
}
};
};
#endif //#ifndef _ZEN_EXTEND_STD_CYC_DEQUE_H_