一个简单实用的循环buffer,用于缓冲数据!测试500M数据,耗时1.3秒。

 循环buffer,即环形缓冲区,设有固定的大小,被定义成一个环形,新数据会覆盖旧的数据,减少内存拷贝,提高程序的性能。适用于通信上接收流式数据,然后进行分片、组包。

    逻辑示意图:

图1

    小C自己实现的环形Buffer,“一写一读”,支持多线程且无锁设计,支持随机长度读写。测试性能:500M 数据用时=1382110us(1.38秒)  速率=2.83Gbps。

    程序设计:

    虽然该程序被称为环形Buffer,实际上在内存中就是一个大的数组,通过对指向数组的位置进行管理,设计成一个环形的队列。

    注:下文所述的指针不是常规C、C++上的(char* p, int* pi)这种指针,而是相对于一个数组起始位置的偏移。例如char buf[32], 偏移10个字节,用一个int pos变量去指向第10字节位置,这个pos在文中被称为指针。

//例如:char buffer[32];int pos=10;buffer[10] == *(buffer+pos)//这两个是完全相等的//这个pos在文中被称为指针,即偏移,此处指针值为10
  1. 写入数据设计示意图

        如图2所示,当我们要写入数据时,我们会关注写指针的位置,还有能写入的空间,图中的白色与黄色区域均是可以写入数据的,黄色区域已经读取了数据,即可被覆盖。

    1. 写指针在前(相对于读指针来看)

图2 写指针在前

        2.写指针在后(相对于读指针位置来看)

    如图3所示,此时写指针已经写的大小是10+3个区域大小,即已经走了一圈且再次走到3这个位置了。现在可以写入的数据大小即是黄色的区域。

图3 写指针在后

综上两种情况,要计算每次可写入数据的长度方式如下:

int wpos;//写指针int rpos;//读指针int buf_size; //整个buffer的大小int available_len = ((rpos - wpos) +buf_size) % buf_size;//计算可写入数据长度//使用读指针位置减去写指针的位置加上总长,就保证该值是一个正数//然后再对这个数进行取余,保证整个大小是在buf_size的范围内//这样即算出可使用的空间大小/******************************************************************///写函数实现/******************************************************************/int Ringbuffer::write(uint8_t* buffer, int len){    register int available_len = ((rpos-wpos)+buffer_size)&buffer_size_mask;    if(available_len <= len){        if(!is_init){            is_init = true;            goto run;        }        return -1;    }run:    register int left_len = MIN(len, buffer_size-wpos);    ::memcpy(m_buffer+wpos, buffer, left_len);    ::memcpy(m_buffer,buffer+left_len, len-left_len);        smp_mb();    wpos += len;    wpos &= buffer_size_mask;
    return len;}//wpos &= buffer_size_mask;  ==> wpos %= buffer_size_mask;//因为buffer长度传入时候处理为2的次幂,因此进行区域的使用可以使用&进行操作//例如:pos % 8 ==> pos&(8-1) 即 pos&7;  //buffer_size_mask = buffer_size -1;

    2.读入数据设计示意图

    i.读指针在后(相对于写指针位置)

        图4是读指针在后,此时可读的数据长度是2-6,即图中绿色区域。

图4 读指针在写指针之后

    ii.读指针在后(相对于写指针位置)

        图5是读指针在前,此时可读区域7-10、0-3,即图中绿色区域。

图5 读指针在写指针之前

(大家有没有发现图4、5 与 图2、3一样,对!就是一样的,只是现在我们要站在读指针的位置去看)

综上两种情况,要计算每次可写入数据的长度方式如下:

int wpos;//写指针int rpos;//读指针int buf_size; //整个buffer的大小int available_len = ((wpos - rpos) +buf_size) % buf_size;//计算可写入数据长度//使用写指针位置减去读指针的位置加上总长,就保证该值是一个正数//然后再对这个数进行取余,保证整个大小是在buf_size的范围内//这样即算出可使用的空间大小注:与上述计算可写长度类似,但区别是此处(写-读)
/*****************************************************************///读函数实现/*****************************************************************/int Ringbuffer::read(uint8_t* buffer, int len){    register int available_len = ((wpos-rpos)+buffer_size)&buffer_size_mask;    if(available_len <= len) return -1;        register int left_len=MIN(len, buffer_size-rpos);    ::memcpy(buffer, m_buffer+rpos, left_len);    ::memcpy(buffer+left_len, m_buffer, len-left_len);    smp_mb();    rpos += len;    rpos &= buffer_size_mask;
    return len;}

        

测试思路:    

  1. 代码测试:

    使用随机生成一个500M的源数据文件,开启两个线程,一个线程从数据文件中随机长度读取数据,然后往循环buffer里面写数据,一个线程从循环buffer里面随机长度读取出数据,然后写入文件,最后使用Beyond Compare软件对两个文件进行比较,查看数据是否相同。

    测试结果:两个500M的文件数据一样,

    1. 测试速率:

 

代码目录简介:

testfile文件夹:生成随机的源数据文件src.dat,已经测试生成的out.dat数据文件

main.cpp文件:主要程序入口

主要功能实现:ringbuffer.cpp ringbuffer.h

 

使用:

    请先进入testfile目录使用gcc generate_bin_file.cpp -o test编译,然后执行./test,生成src.dat源数据文件。然后回到上级目录,执行make,生成mainApp, 执行mainApp便可以测试程序。运行环境:Linux系统的任意发行版本都可以。

想要整个工程文件的小伙伴:请关注微信关注【Linux编程用C】,在微信公众号回复 ringbuffer

我是小C,欢迎大家关注、点赞支持,我们一起交流讨论学习!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值