前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Tars-C++ 揭秘篇:TC_Buffer的妙用

Tars-C++ 揭秘篇:TC_Buffer的妙用

原创
作者头像
路小饭
修改2019-01-12 09:47:33
1.6K0
修改2019-01-12 09:47:33
举报

在上篇文章Tars-C++ 揭秘篇:Tars-RPC收发包管理中,客户端收发包流程的缓存都用到了TC_Buffer结构,利用“水位”完成了内存的动态管理。本章对其进行介绍

11.1 TC_Buffer的整体结构

TC_Buffer的本质是一个字符串char* _buffer,我们使用_readPos读游标和_wirtePos写游标标明了已读数据和未读数据,使用_capacity标明整个_buffer当前大小。如下图所示:

11.1TC_Buffer结构.png
11.1TC_Buffer结构.png
  • 红色方框里是已读数据,绿色方框是未读数据,淡紫色方框是已申请但未占用的空间

11.2 TC_Buffer原理说明

还是以具体场景说明TC_Buffer的工作原理。

(1)TC_Buffer初始化时,_readPos、_writePos、_capacity都为0,_buffer为NULL

(2)通过PushData放入第一笔数据data,长度为size,TC_Buffer默认_capacity为128字节。这时分两种情况:

  • 如果size < _capacity,直接创建大小为128字节的_buffer,然后将data拷贝到_buffer中,并调整写游标位置。如下图:
11.2小于128字节.png
11.2小于128字节.png
  • 如果size > _capacity,即size大于128字节,则_capacity会以2的n次方形式增长(跟vector的内存管理很像),直到_capacity > size。例如size为200,_capacity = 2的8次方= 256字节。如果size为300,_capacity = 2的9次方 = 512字节。当size = 200时候,_buffer如下图:
11.2大于128字节.png
11.2大于128字节.png

(3) 假设第2步已经放入了size为200的数据data,现在通过PopData读取长度为size1 = 100的数据。完成后,_buffer的游标数据如下:

11.2PopData.png
11.2PopData.png

(4)上面第3步后,_buffer的缓冲区还剩下256 - 200 = 56 字节的空间(淡紫色方框)。这时通过PushData放入第二笔数据data2,同样分两种情形,数据长度size2 > 56;数据长度size2 < 56

  • size2 = 60,大于56。这时_capacity = 256需要再翻倍,变成512字节。同时放弃原有老_buffer,把之前未读内容(长度为_writePos - _readPos)拷贝到新的_buffer上。如下图:
11.2第二次PushData大于56.png
11.2第二次PushData大于56.png
  • size2 = 50,小于 56。会利用::memmove方法把未读内容重新拷贝到原有_buffer第一位,覆盖掉已读内容。然后加上新的data2内容。如下图:
11.2第二次PushData小于56.png
11.2第二次PushData小于56.png

(5)第5步我们单独讨论Shrink。可能有小伙伴已经发现了,如果像上面步骤一直PushData PopData,_capacity会一直增加不会减少,即使数据都已经被读取完了。这时候Shrink就派上用场了。

  • 如果_capacity小于128默认字节,不会进行内存调整。
  • 如果未读数据大于整体容量和水位(_highWaterPercent)乘积,不会进行内存调整,反之会进行。
  • 例如未读数据size = 150(_writePos - _readPos),整体容量_capacity = 256,_highWaterPercent为50,会进行下面的判断:150 100 > 256 50,即不进行内存调整
  • 例如未读数据size = 100(_writePos - _readPos),整体容量_capacity = 256,_highWaterPercent为50,则有100 100 < 256 50,这时候进行内存调整。调整策略是:使用函数RoundUp2Power创建一个大小为128(2 2 2 2 2 2 2)的新_buffer,将未读数据拷贝到新_buffer,后续使用这个新_buffer.
11.2Shrink.png
11.2Shrink.png

11.3 小结

  • Shrink()函数的使用时机是人为控制的,可以设置各种触发条件,如未读数据大小为0时候,或者整体容量_capacity大于某个数值时候
  • 每次的PushData相当于清理了已读数据,释放出了这部分空间供新数据使用
  • 当新数据很长,超过了当前的可用空间(_capacity - _writePos)时,整体容量_capacity的扩容策略是取大于当前_capacity的最小的2的n次方的值。这样的好处是预先分配了内存空间,可以减少内存的频繁申请。
  • _highWaterPercent,即水位值。在Tars RPC取的默认值是50,即未读数据的长度大于整体容量一半时候,是不调整内存的,而小于整体容量一半时候,说明空间比较浪费,需要重新调整内存了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 11.1 TC_Buffer的整体结构
  • 11.2 TC_Buffer原理说明
  • 11.3 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档