ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

高性能环形缓冲区设计

2021-07-15 19:00:04  阅读:179  来源: 互联网

标签:lwsBuffer int 环形 ret dataLen 高性能 缓冲区 data writepos


环形缓冲区设计分两种模式

来源:微信公众号「编程学习基地」

文章目录

模式一

写入读取数据,不考虑读取数据的长度,读取数据的顺序为写入数据的顺序

环形缓冲区测试代码

#include "lwsBuffer.h"
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
    char dataBuf[1024];
    void *temp = NULL;
    int readFd = open("lwsBuffer.cpp", O_RDONLY);
    int writeFd = open("temp.cpp", O_WRONLY | O_CREAT);

    int ret;
    memset(dataBuf, '\0', 1024);
    lwsBuffer Buff;
    while (1)
    {
        /* code */
        memset(dataBuf, '\0', sizeof(dataBuf));
        ret = read(readFd, dataBuf, sizeof(dataBuf));
        if (ret == 0)
        {
            printf("read end..");
            break;
        }
        else if (ret < 0)
            printf("read error ret:%d", ret);
        else
            printf("read  ret:%d\n", ret);
        ret = Buff.write(dataBuf, ret); //将 dataBuf 数据写入到Buff缓冲区
        printf("write ret:%d\n", ret);
    }
    printf("\n\ncurrent Buff total len:%d\n\n", Buff.getTotalLen());
    while (1)
    {
        char tempBuf[1024];
        memset(tempBuf,'\0',sizeof(tempBuf));
        ret = Buff.read(&temp); //从 Buff缓冲区中读取数据
        if (ret == -1)
            break;
        memmove(tempBuf, temp, ret);
        /* code */
        printf("total len:%d\n", Buff.getTotalLen());
        write(writeFd, tempBuf, ret);
    }

    close(readFd);
    close(writeFd);
    return 0;
}

makefile编译文件

test:test.cpp
	g++ -o test test.cpp lwsBuffer.cpp 
.PHONY:clean
clean:
	rm -f test temp.cpp

运行结果

sh-4.3$ make
g++ -o test test.cpp lwsBuffer.cpp 
sh-4.3$ ls
lwsBuffer.cpp  lwsBuffer.h  makefile  test  test.cpp
sh-4.3$ ./test 
read  ret:1024
write ret:1024
read  ret:1024
write ret:1024
read  ret:1024
write ret:1024
read  ret:964
write ret:964
read end..

current Buff total len:4036

total len:3012
total len:1988
total len:964
total len:0
sh-4.3$ ls
lwsBuffer.cpp  lwsBuffer.h  makefile  temp.cpp  test  test.cpp

执行后结果就是读取 lwsBuffer.cpp 里面的数据,储存到环形缓冲区,再从环形缓冲区读取数据写入到 temp.cpp

应用场景和优缺点

大量数据的转发

优点:牺牲少量内存实现最少的数据拷贝memmove,极大的提高服务性能。理论上每个数据块只需要一次拷贝。

缺点:当转发的数据很大时,没一个对象浪费的内存在转发的数据大小之内

​ 当读取数据缓慢的时候会造成频繁的内存重分配,缓冲区变得越来越大。

lwsBuffer.h

#pragma once

#ifndef _LWS_BUFFER_H_
#define _LWS_BUFFER_H_
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <list>
#define BUFFER_SIZE 1024
class lwsBuffer
{
private:
    /* data */
    void* buffer;               /* 数据 */
    size_t bufMaxSize;          /* 最大的存储数据大小 */
    size_t rightWritePos;
    int readpos,writepos;       /* 读写位置 */

    size_t totalLen;            /* 存储的数据大小 */
    std::list<int> lenList;     /* 数据长度的链表 */
public:
    lwsBuffer(/* args */);
    ~lwsBuffer();
public:
    int write(void *data, int dataLen);
    int read(void** data);
    int getTotalLen();
protected:
    int writeAfter(void* data, int dataLen);
    int writePre(void* data, int dataLen);
    void _remalloc();
};
#endif

lwsBuffer.cpp

#include "lwsBuffer.h"

lwsBuffer::lwsBuffer(/* args */)
{
    this->readpos = 0;
    this->writepos = 0;
    this->rightWritePos = 0;
    this->bufMaxSize = BUFFER_SIZE;
    this->buffer = malloc(this->bufMaxSize);

    this->totalLen = 0;
    this->lenList.clear();
}

lwsBuffer::~lwsBuffer()
{
    if (buffer)
        free(buffer);
}

int lwsBuffer::write(void *data, int dataLen)
{
    int ret = -1;
    if (dataLen <= 0 || data == NULL)
        return ret;

    /* writePos 在 readPos 前 / readPos == writePos push*/
    if (this->writepos >= this->readpos)
    {
        ret = writePre(data, dataLen);
    }
    /* readPos 在 writePos 前  push*/
    else if(this->writepos < this->readpos)
    {
        ret = writeAfter(data, dataLen);
    }
    return ret;
}

int lwsBuffer::writePre(void* data, int dataLen)
{
    int ret = -1;
    /*  写入位置在前  */
    /*  buffer 后面有空间可以写入数据*/
    if ((this->writepos + dataLen) <= this->bufMaxSize) 
    {
        //将数据写入到 buffer 里面去
        memmove((char *)this->buffer + this->writepos, data, dataLen);
        /* 修改 writepos 偏移 */
        this->writepos = this->writepos + dataLen;
        /* 计算数据长度*/
        lenList.push_back(dataLen);
        totalLen += dataLen;
        ret = dataLen;
    }
    else
    {
        /*  buffer 后面没有空间可以写入数据  记录下罪*/
        this->rightWritePos = this->writepos;
        /* 修改 writepos 偏移 */
        this->writepos = 0;
        ret = writeAfter(data, dataLen);
    }
    return ret;
}

int lwsBuffer::writeAfter(void *data, int dataLen)
{
    int ret = -1;
    /*  写入位置在后  */
    /*  writepos 到 readpos 有足够的空间可以写入数据  */
    if ((this->writepos + dataLen) < this->readpos)
    {
        //将数据写入到 buffer 里面去
        memmove((char *)this->buffer + this->writepos, data, dataLen);
        /* 修改 writepos 偏移 */
        this->writepos = this->writepos + dataLen;
        /* 计算数据长度*/
        lenList.push_back(dataLen);
        totalLen += dataLen;
        ret = dataLen;
    }
    else
    {
        this->_remalloc();
        ret = writePre(data, dataLen);
    }
    return ret;
}

void lwsBuffer::_remalloc()
{
    /*  数据满了 */
    void *newBuf = malloc(this->bufMaxSize + BUFFER_SIZE / 2);
    /*      */
    memmove(newBuf, (char*)this->buffer + this->readpos, this->rightWritePos - this->readpos);
    memmove((char*)newBuf + this->rightWritePos - this->readpos, this->buffer, this->writepos);
    free(this->buffer);
    this->buffer = newBuf;

    this->writepos = this->rightWritePos - this->readpos + this->writepos;
    this->readpos = 0;
    this->bufMaxSize = this->bufMaxSize + BUFFER_SIZE / 2;
    // printf("buffMaxSize:%d, totalLen:%d ,list size:%d\n", (int)this->bufMaxSize, (int)this->totalLen, (int)lenList.size());
}

int lwsBuffer::read(void** data)
{
    int ret = -1;
    if (this->totalLen > 0)
    {
        int dataLen = lenList.front();
        if((this->readpos + dataLen) <= this->bufMaxSize)
        {
            (*data) = (char *)this->buffer + this->readpos;
            // printf("\n读取位置: %d ,读取信息 len: %d, data:%s\n", this->readpos, dataLen, (char*)data);
            /* 修改 writepos 偏移 */
            this->readpos += dataLen;
            /* 计算数据长度*/
            this->totalLen -= dataLen;
            lenList.pop_front();
            ret = dataLen;
        }
        else{
            printf("从头开始读取\n");
            this->readpos = 0;
            (*data) = (char *)this->buffer + this->readpos;

            // printf("\n读取位置: %d ,读取信息 len:%d, data:%s\n", this->readpos, dataLen, (char*)data);
            this->readpos += dataLen;
            /* 计算数据长度*/
            this->totalLen -= dataLen;
            lenList.pop_front();
            ret = dataLen;
        }
    }
    return ret;
}

int lwsBuffer::getTotalLen()
{
    return this->totalLen;
}

模式二

写入读取数据,写入指定长度数据,和读取指定长度数据,模仿QBuffer的读写功能

环形缓冲区测试代码

#include "lwsBuffer.h"
#include<stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    char buf[1024],temp[1024];
    int readFd = open("lwsBuffer.cpp",O_RDONLY);
    int writeFd = open("temp.cpp",O_WRONLY|O_CREAT,0666);
    
    int ret;
    memset(buf, '\0', 1024);
    memset(temp, '\0', 1024);
    lwsBuffer Buff;
    while (1)
    {
        /* code */
        ret = read(readFd, buf, sizeof(buf));
        if (ret == 0)
            break;
        int dataLen = Buff.write(buf,ret);  //将数据保存到环形缓冲区
        memset(buf, '\0', 1024);
        printf("dataLen:%d\n",dataLen);
    }

    while(1)
    {
        memset(temp,'\0',sizeof(temp));
        ret = Buff.read(temp, 1024);    //从环形缓冲区中读取数据
        if (ret == -1)
            break;
        /* code */
        ret = write(writeFd, temp, ret);
        printf("write ret:%d\n", ret);
    }

    close(readFd);
    close(writeFd);
    return 0;
}

makefile编译文件

test:test.cpp
	g++ -o test test.cpp lwsBuffer.cpp 
.PHONY:clean
clean:
	rm -f test temp.cpp

运行结果

sh-4.3$ make
g++ -o test test.cpp lwsBuffer.cpp 
sh-4.3$ ./test 
dataLen:1024
dataLen:1024
dataLen:1024
dataLen:766
write ret:1024
write ret:1024
write ret:1024
write ret:766

执行后结果就是读取 lwsBuffer.cpp 里面的数据,储存到环形缓冲区,再从环形缓冲区读取数据写入到 temp.cpp

应用场景和优缺点

适用于少量数据的频繁读写

优点:多次memmove实现占空间最少的快速读写

缺点:当转发的数据很大且很频繁时,多次memmove会导致性能的减少

lwsBuffer.h

#pragma once

#ifndef _LWS_BUFFER_H_
#define _LWS_BUFFER_H_
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BUFFER_SIZE 1024
class lwsBuffer
{
private:
    /* data */
    void* buffer;               /*数据*/
    size_t bufMaxSize;          /* 最大的存储数据大小 */
    size_t bufLen;              /* 存储的数据大小 */
    int readpos,writepos;       /* 读写位置 */
public:
    lwsBuffer(/* args */);
    ~lwsBuffer();
public:
    int write(void *data, int dataLen);
    int read(void *data, int dataLen);

protected:
    int writeAfter(void* data, int dataLen);
    int writePre(void* data, int dataLen);
    void _remalloc();
};

#endif

lwsBuffer.cpp

#include "lwsBuffer.h"

lwsBuffer::lwsBuffer(/* args */)
{
    this->readpos = 0;
    this->writepos = 0;
    this->bufLen = 0;
    this->bufMaxSize = BUFFER_SIZE;
    this->buffer = malloc(this->bufMaxSize);
}

lwsBuffer::~lwsBuffer()
{
    if (buffer)
        free(buffer);
}

int lwsBuffer::write(void *data, int dataLen)
{
    int ret = -1;
    /* writePos 在 readPos 前 / readPos == writePos push*/
    if (this->writepos >= this->readpos)
    {
        ret = writePre(data, dataLen);
    }
    /* readPos 在 writePos 前  push*/
    else if(this->writepos < this->readpos)
    {
        ret = writeAfter(data, dataLen);
    }
    return ret;
}

int lwsBuffer::writePre(void* data, int dataLen)
{
    int ret = -1;
    /*  写入位置在前  */
    /*  buffer 后面有空间可以写入数据  预留一个字节不存储数据,用于区分*/
    if ((this->writepos + dataLen) <= this->bufMaxSize)
    {
        //将数据写入到 buffer 里面去
        memmove((char *)this->buffer + this->writepos, data, dataLen);
        // printf("即将 push %d 消息 on:%d :%s\n", dataLen, prod->writepos, (char *)prod->buffer + prod->writepos + LWS_PRE);
        this->writepos = this->writepos + dataLen;
        ret = dataLen;
        bufLen += dataLen;
    }
    else
    {
        /*  buffer 后面没有空间可以写入数据  记录下*/
        int temp = this->bufMaxSize - this->writepos;
        temp = writePre(data, temp);
        this->writepos = 0;
        ret = writeAfter((char*)data + temp, dataLen - temp) + temp;
    }
    return ret;
}

int lwsBuffer::writeAfter(void *data, int dataLen)
{
    int ret = -1;
    /*  写入位置在后  */
    /*  writepos 到 readpos 有足够的空间可以写入数据  */
    if ((this->writepos + dataLen) < this->readpos)
    {
        //将数据写入到 buffer 里面去
        memmove((char *)this->buffer + this->writepos, data, dataLen);
        this->writepos = this->writepos + dataLen;
        ret = dataLen;
        bufLen += dataLen;
    }
    else
    {
        this->_remalloc();
        ret = writePre(data, dataLen);
    }
    return ret;
}

void lwsBuffer::_remalloc()
{
    /*  数据满了 */
    void *newBuf = malloc(this->bufMaxSize + BUFFER_SIZE / 2);
    /*      */
    memmove(newBuf, (char*)this->buffer + this->readpos, this->bufMaxSize - this->readpos);
    memmove((char*)newBuf + this->bufMaxSize - this->readpos, this->buffer, this->writepos);
    free(this->buffer);
    this->buffer = newBuf;

    this->writepos = this->bufMaxSize - this->readpos + this->writepos;
    this->readpos = 0;
    this->bufMaxSize = this->bufMaxSize + BUFFER_SIZE / 2;
    // printf("buffMaxSize:%d, bufLen:%d\n", (int)this->bufMaxSize,(int)this->bufLen);
}

int lwsBuffer::read(void *data, int dataLen)
{
    int ret = -1;
    if (this->bufLen > 0)
    {
        if (dataLen > this->bufLen)
        {
            dataLen = this->bufLen;
        }
        if ((this->readpos + dataLen) <= this->bufMaxSize)
        {
            memmove(data, (char *)this->buffer + this->readpos, dataLen);
            // printf("\n读取位置: %d ,读取信息 len:%d,data:%s\n", this->readpos, dataLen, (char *)data);
            this->readpos += dataLen;
            this->bufLen -= dataLen;
            ret = dataLen;
        }
        else
        {
            int temp = this->bufMaxSize - this->readpos;
            temp = read(data, temp);
            this->readpos = 0;
            
            memmove((char*)data + temp, (char *)this->buffer, dataLen - temp);
            // printf("\n读取位置: %d ,读取信息 len:%d, data:%s\n", this->readpos, dataLen, (char *)data);
            this->readpos = this->readpos + dataLen - temp;
            this->bufLen = this->bufLen - (dataLen - temp);
            ret = dataLen;
        }
    }
    return ret;
}

标签:lwsBuffer,int,环形,ret,dataLen,高性能,缓冲区,data,writepos
来源: https://blog.csdn.net/qq_44519484/article/details/118766189

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有