首页 > 系统相关 >brpc内存管理

brpc内存管理

时间:2023-02-05 13:34:04浏览次数:41  
标签:bthread ResourcePool brpc 管理 对象 偏移量 内存 分配

内存管理总是程序中的重要一环,在多线程时代,一个好的内存分配大都在如下两点间权衡:

  • 线程间竞争少。内存分配的粒度大都比较小,对性能敏感,如果不同的线程在大多数分配时会竞争同一份资源或同一把锁,性能将会非常糟糕,原因无外乎和cache一致性有关,已被大量的malloc方案证明。
  • 浪费的空间少。如果每个线程各申请各的,速度也许不错,但万一一个线程总是申请,另一个线程总是释放,内存就爆炸了。线程之间总是要共享内存的,如何共享就是方案的关键了。

一般的应用可以使用tcmalloc、jemalloc等成熟的内存分配方案,但这对于较为底层,关注性能长尾的应用是不够的。多线程框架广泛地通过传递对象的ownership来让问题异步化,如何让分配这些小对象的开销变的更小是值得研究的问题。其中的一个特点较为显著:

大多数结构是等长的。

这个属性可以大幅简化内存分配的过程,获得比通用malloc更稳定、快速的性能。brpc中的ResourcePool和ObjectPool即提供这类分配。

这篇文章不鼓励用户使用ResourcePool或ObjectPool,事实上我们反对用户在程序中使用这两个类。因为”等长“的副作用是某个类型独占了一部分内存,这些内存无法再被其他类型使用,如果不加控制的滥用,反而会在程序中产生大量彼此隔离的内存分配体系,既浪费内存也不见得会有更好的性能。

ResourcePool

创建一个类型为T的对象并返回一个偏移量,这个偏移量可以在O(1)时间内转换为对象指针。这个偏移量相当于指针,但它的值在一般情况下小于2^32,所以我们可以把它作为64位id的一部分。对象可以被归还,但归还后对象并没有删除,也没有被析构,而是仅仅进入freelist。

下次申请时可能会取到这种使用过的对象,需要重置后才能使用。

当对象被归还后,通过对应的偏移量仍可以访问到对象,即ResourcePool只负责内存分配,并不解决ABA问题。但对于越界的偏移量,ResourcePool会返回空。

由于对象等长,ResourcePool通过批量分配和归还内存以避免全局竞争,并降低单次的开销。每个线程的分配流程如下:

  1. 查看thread-local free block。如果还有free的对象,返回。没有的话步骤2。
  2. 尝试从全局取一个free block,若取到的话回到步骤1,否则步骤3。
  3. 从全局取一个block,返回其中第一个对象。

原理是比较简单的。工程实现上数据结构、原子变量、memory fence等问题会复杂一些。

ObjectPool

这是ResourcePool的变种,不返回偏移量,而直接返回对象指针。内部结构和ResourcePool类似,一些代码更加简单。对于用户来说,这就是一个多线程下的对象池,brpc里也是这么用的。比如Socket::Write中把每个待写出的请求包装为WriteRequest,这个对象就是用ObjectPool分配的。

生成bthread_t

用户期望通过创建bthread获得更高的并发度,所以创建bthread必须很快。 在目前的实现中创建一个bthread的平均耗时小于200ns。如果每次都要从头创建,是不可能这么快的。创建过程更像是从一个bthread池子中取一个实例,我们又同时需要一个id来指代一个bthread,所以这儿正是ResourcePool的用武之地。bthread在代码中被称作Task,其结构被称为TaskMeta,定义在task_meta.h中,所有的TaskMeta由ResourcePool分配。

bthread的大部分函数都需要在O(1)时间内通过bthread_t访问到TaskMeta,并且当bthread_t失效后,访问应返回NULL以让函数做出返回错误。

解决方法是:bthread_t由32位的版本和32位的偏移量组成。版本解决ABA问题,偏移量由ResourcePool分配。查找时先通过偏移量获得TaskMeta,再检查版本,如果版本不匹配,说明bthread失效了。

注意:这只是大概的说法,在多线程环境下,即使版本相等,bthread仍可能随时失效,在不同的bthread函数中处理方法都是不同的,有些函数会加锁,有些则能忍受版本不相等。

这种id生成方式在brpc中应用广泛,brpc中的SocketId,bthread_id_t也是用类似的方法分配的。

使用ResourcePool加快创建的副作用是:一个pool中所有bthread的栈必须是一样大的。

这似乎限制了用户的选择,不过基于我们的观察,大部分用户并不关心栈的具体大小,而只需要两种大小的栈:尺寸普通但数量较少,尺寸小但数量众多。

所以我们用不同的pool管理不同大小的栈,用户可以根据场景选择。两种栈分别对应属性BTHREAD_ATTR_NORMAL(栈默认为1M)和BTHREAD_ATTR_SMALL(栈默认为32K)。server默认使用BTHREAD_ATTR_NORMAL运行用户代码。

goroutine在1.3前通过segmented stacks动态地调整栈大小,发现有hot split问题后换成了变长连续栈(类似于vector resizing,只适合内存托管的语言)。由于bthread基本只会在64位平台上使用,虚存空间庞大,对变长栈需求不明确。加上segmented stacks的性能有影响,bthread暂时没有变长栈的计划。

标签:bthread,ResourcePool,brpc,管理,对象,偏移量,内存,分配
From: https://www.cnblogs.com/lygin/p/17093249.html

相关文章

  • Linux系统之安装Linux管理工具inpanel
    (Linux系统之安装Linux管理工具inpanel)一、inpanel介绍1.inpanel简介inpanel是一款一个基Web的Linux开源管理工具。这个项目的目标是做出最简单、最快速、最无痛的Li......
  • Linux系统之部署MxsDoc个人文件管理系统
    (Linux系统之部署MxsDoc个人文件管理系统)一、MxsDoc介绍1.MxsDoc简介MxsDoc是基于Web的开源文件管理系统。2.MxsDoc功能支持权限管理历史版本管理Office预览/编......
  • qtableview及自定义model的使用,对比qtablewidget性能及内存优化(转)
    以前一直使用qtableiwdget,最近有时间来研究下qtableview,才知道,qtableview和自定义model,比qtablewidget的性能啊,及占用内存啊,优化太多了。以前我使用qtablewidget是进行动态......
  • C++学生信息管理系统[2023-02-05]
    C++学生信息管理系统[2023-02-05]25、学生信息管理系统设计要求实现如下功能:1.建立学生信息数据,包括学号、姓名、性别、三科成绩、出生时间、年龄(由出生时间计算得到)......
  • C/C++图书管理系统[2023-02-05]
    C/C++图书管理系统[2023-02-05]选题二十三:图书管理系统【问题描述】设计一个计算机管理系统完成图书管理基本业务。 【任务要求】(1)每种书的登记内容包括书号、书名、......
  • C/C++内存对齐原则
    C/C++内存对齐what&&why当用户自定义类型时(struct或class),编译器会自动计算该类型占用的字节数。C/C++为什么要内存对齐?我道行太浅,摘抄了网上的一个解释。为了方......
  • 5.4节约内存的编程方式
    以图形用户界面(GUI,GraphicalUserInterface)为基础的Windows,可以说是一个巨大的操作系统。Windows的前身是MS-DOS操作系统,最初版本可以在128KB左右的内存上运行,而想要W......
  • 内存和磁盘的亲密关系——5.1不读入内存就无法运行
    1.存储程序方式指的是什么?在存储装置中保存程序,并逐一运行的方式2.通过使用内存来提高磁盘访问速度的机制称为什么?DiskCache(磁盘缓存)3.把磁盘的一部分作为假想内存来......
  • 5.3虚拟内存把磁盘作为部分内存来使用
    接下来说一下虚拟内存虚拟内存是指把磁盘的一部分作为假想的内存来使用。这与磁盘缓存是假想的磁盘(实际上是内存)相对,虚拟内存是假想的内存(实际上是磁盘)。通过借助虚拟......
  • 熟练使用有棱有角的内存——4.1内存的物理机制很简单
    1.有十个地址信号引脚的内存IC(集成电路)可以指定的地址范围是多少?答:用二进制数来表示的话是0000000000~1111111111(用十进制数来表示的话是0~1023)2.高级编程语言中的数......