使用自己的内存管理方式
最近做监控展示,接收包,解码,播放,多画面短时间轮询,例如10秒,在频繁使用申请内存和删除内存的过程中,不可避免产生性能上的损耗,操作系统在无法申请到内存时,如果自己的程序没有判断申请内存是否成功,后面会产生一系列的问题,最好的解决方案是使用自己的内存管理方式。
定义几个数据结构
enum
{
en_emptying = 0,
en_writing,
en_canreadings
};
struct smem
{
char v_status;
char v_i_flag;
char r1;
char r2;
int v_len = 0;
uint32_t v_pts = 0;
uint8_t *v_data = NULL;
};
typedef std::shared_ptr<smem> ptr_smem;
定义内存池
包含这样的数据指针:
1 数据内存头部
2 数据内存结尾
3 写指针
4 读指针
我们限定只有一个线程读,一个线程写,并且避开读写内存管理的锁定。
我们的数据有两种,一种是数据信息info,也就是我们需要的内存起始,内存长度,以及其他附加数据信息,比如这个包的pts值,这个包的状态信息,是否是关键帧等等,这些东西我们必须要做内存对齐规整,另一种数据就是实际数据,比如h264包,h265包,需要解码的视频数据(或者其他数据)
这其中有两种方式来做这个池,一个是将数据信息直接写入内存的起始位置,一种是将数据信息放入队列,直接放入内存池中也是可以的,这种写法避免了信息队列的维护,当然,信息队列已经是很小的内存了,我们提出两种方式,可以根据需求选择。
方式一,写入内存池
v_write 是内存写入地址,每次写完就往后跳,写入时首先要知道不能超过剩余的空间,否则要等读指针结束,或者让读指针快速通过。写入方法:
uint8_t *m = v_write;
*(char*)(m) = (char)en_emptying;
m +=sizeof(char);
*(char*)(m) = (char)0;
m +=sizeof(char)*3;
*(int*)m = 0;
m +=sizeof(int);
*(uint32_t*)m = 0;
以上方法就是将信息数据写入内存池的数据头部,然后再写入数据。这种方式比较考验程序员的基本功,其实也不推荐,另外一种方式直接写入队列中,逻辑清晰,该队列只是一个小型数据集,不会引起性能波动。
以下使用队列和内存池来存放内存
看代码吧,读写指针一定要离开一定的距离,开始时是在一起的,以下代码已经经过验证,当然,在使用的时候还是需要一定的技巧的。
struct s_mem_pool
{
//qianbo :just one thread read,one thread write ,otherwise error occur
uint8_t *v_data = NULL;
uint8_t *v_end = NULL;
int v_len = 0;;
uint8_t *v_write = NULL;
uint8_t *v_read = NULL;
QMutex v_mux;
std::atomic_int v_framenum;
std::queue<ptr_smem>v_i;
void init_mem(int memlen)
{
if(v_data!=NULL)
{
if(v_len < memlen)
{
free(v_data);
v_data = NULL;
}
}
v_len = memlen;
if(v_data == NULL)
v_data = (uint8_t*)malloc(memlen);
v_end = v_data + memlen;
v_write = v_data;
v_read = v_data;
v_framenum = 0;
v_mux.lock();
while(!v_i.empty())
{
v_i.pop();
}
v_mux.unlock();
~s_mem_pool()
{
clearqueue();
if(v_data!=NULL)
free(v_data);
}
bool push(uint8_t *data, int len,uint32_t pts,bool i_flag)
{
#define EX_LEN (sizeof(char)*4 +sizeof(int)+sizeof(uint32_t))
#define NEEDLEN (len)
#define WRITE_INFO \
ptr_smem mem = std::make_shared<smem>();\
mem->v_data = v_write;\
mem->v_len = len;\
mem->v_pts = pts;\
mem->v_i_flag = i_flag; \
mem->v_status = en_writing;\
v_mux.lock();\
v_i.emplace(mem);\
v_mux.unlock();
#define WRITE_INFO2 \
uint8_t *m = v_write; \
*(char*)(m) = (char)en_writing; \
m +=1; \
*(char*)(m) = (char)i_flag;\
m +=3; \
*(int*)m = len; \
m +=4; \
*(uint32_t*)m = pts; \
m+= 4;\
v_write = m;
if(v_write >= v_read)
{
if((v_end - v_write) > (long)(NEEDLEN))
{
memcpy(v_write,data,len);
WRITE_INFO
v_write += (NEEDLEN);
v_framenum++;
return true;
}
else // the left mem is not enough
{
//qInfo()<<"left mem is not enough";
if((v_read -v_data) > (long)(NEEDLEN))
{
v_write = v_data;
memcpy(v_write,data,len);
WRITE_INFO
v_write += NEEDLEN;
v_framenum++;
return true; //from the head
}
else
{
qInfo()<<"not enough memory 0 :num"<<v_framenum;
return false;
}
}
}
else //read>write
{
if((v_read - v_write) > (long)(NEEDLEN))
{
memcpy(v_write,data,len);
WRITE_INFO
v_write += (NEEDLEN);
v_framenum++;
//IncNumber();
return true;
}
qInfo()<<"not enough memory 1 clear the buffer the len is "<< NEEDLEN;
return false;
}
}
ptr_smem get()
{
ptr_smem sm = nullptr;
v_mux.lock();
if(!v_i.empty())
{
sm = v_i.front();
v_i.pop();
v_read = sm->v_data;
}
v_mux.unlock();
v_framenum--;
return sm;
int size()
{
return v_framenum;
}
void clearqueue()
{
v_mux.lock();
if(!v_i.empty())
{
v_i.pop();
}
v_mux.unlock();
}
};