jemalloc 5.3.0的tsd模块的源码分析
一、背景
在主流的内存库里,jemalloc作为android 5.0-android 10.0的默认分配器肯定占用了非常重要的一席之地。jemalloc的低版本和高版本之间的差异特别大,低版本的诸多网上整理的总结,无论是在概念上和还是在结构体命名上在新版本中很多都找不到,而高版本尤其5.x.x版本就几乎没有现成的网上整理文档。这篇博客作为jemalloc 5.3.0专栏里的第一篇,后续会不断更新jemalloc 5.3.0版本的源码分析及实验对比,揭开jemalloc 5.3.0里的诸多实现上的细节。
这篇博客,我们会先介绍jemalloc 5.3.0里的tsd模块,为什么第一篇jemalloc 5.3.0的博客要介绍这个tsd模块呢?因为在jemalloc 5.3.0里,tsd模块算是一个基础组件模块,不先分析tsd模块,绕开它,会让源码分析困难重重。另外,tsd模块在实现上有不少编程上的技巧,不少技巧对于我们C++尤其C的开发人员也是非常值得借鉴的,尤其tsd模块里的若干宏定义和展开的实现,极简的优化了代码量,这种C的宏展开方式来达到的最终代码执行的效果,虽然从代码阅读上可能会有些晦涩,但是一旦掌握以后,它所带来的代码简化收益会相当可观,实现上可能会比替代用C++的大量的抽象和继承而定义的大量的冗余的类而言,从长期阅读观感上会更为极简。
我们会在第二章里先介绍jemalloc 5.3.0里的tsd模块的实现细节,其底层用到的glibc的tls机制细节可以参考之前的博客 线程局部存储tls的原理和使用-CSDN博客,然后在第三章里,我们对第二章里介绍的一些细节进行提炼和抽象,抽象出可以进行复用的编程上的技巧,作为我们一线开发人员长期编码上的一些参考。
二、jemalloc 5.3.0里的tsd模块的实现细节
我们先在 2.1 里介绍一下jemalloc 5.3.0的代码下载编译,并介绍个人在为了方便调试和调试分析的编译方式和进行jemalloc库的调试。在 2.2 里我们介绍tsd模块的用途和实现原理。
2.1 jemalloc 5.3.0的代码下载、编译和调试
2.1.1 代码下载和编译
jemalloc的github网址:
https://github.com/jemalloc/jemalloc
下载命令:
git clone https://github.com/jemalloc/jemalloc.git
cd jemalloc以后,执行如下命令切换到 5.3.0 版本:
git checkout 5.3.0
2.1.2 为了方便调试分析修改了一下源码和编译参数
关于tsd模块有一个宏定义的名字过长导致变量的名字过长,造成在通过vs2019进行ssh的gdb调试时,显示不出完整的变量名,造成代码分析的障碍,所以,临时为了调试方便,把该宏改短:
在tsd.h里,原始的宏定义如下:

临时改成(下面的名字可按照个人情况随意指定):

改完以后,先要执行自动生成编译用的文件的指令:
./autogen.sh
如果遇到如下错误:

则需要进行autoconf的安装:
apt-get update
apt-get install autoconf
安装完autoconf以后,重新执行./autogen.sh
在make之前,配置一下参数,使用O0,覆盖掉原来的O3:
./configure CFLAGS="-O0"
然后再执行make
如果遇到如下warning:

可以忽略,或者修改一下configure.ac文件里的ARFLAGS,下图是原始的内容:

修改成:

重新执行一遍上面的./autogen.sh ./configure xxx make clean;make -jx的流程以后,就不会遇到“ar: `u' 修饰符被忽略,因为 `D' 为默认(参见 `U')”的错误了。意思就是有了‘D’作为默认,ARFLAGS的‘u’就会被忽略,那就是不需要这个‘u’,去掉即可。
2.1.3 调试jemalloc库
经过上面的编译之后,生成物默认在jemalloc文件夹下的lib目录下,把它们拷贝到/usr/lib下:

然后我们参考 linux上对于so库的调试——包含通过vs2019远程ssh调试so库_vs2019 gdb调试-CSDN博客 这篇博客的方法进行远程ssh进行gdb调试jemalloc的库(博客里举的例子就是调试jemalloc库的例子)。
2.2 tsd模块的用途和实现原理
2.2.1 tsd模块借助的是glibc提供的tls机制
jemalloc 5.3.0里的tsd的意思是指Thread-Specific-Data,用的是之前的博客 线程局部存储tls的原理和使用-CSDN博客 提到的glibc的tls机制来实现的。jemalloc有关tsd的注释:

2.2.2 tsd模块实际用到的就4个文件
tsd模块的源文件就一个是src/tsd.c,头文件有多个:

但是对于x86_64 linux平台,根据tsd.h里下面这段根据编译选项来决定用那个头文件,而不用另外的几个头文件:

上图中,根据增加#err来确定,x86_64 linux平台,用的就是tsd_tls.h头文件。所以tsd模块里我们需要关注的头文件就只有下面这三个:
tsd.h
tsd_tls.h
tsd_types.h
所以,tsd模块在x86_64 linux平台实际用到的就4个文件:
tsd.c tsd.h tsd_tls.h tsd_types.h
2.2.3 tsd模块实现的核心是tsd.h文件,struct tsd_s按照TSD_DATA_SLOW、TSD_DATA_FAST、TSD_DATA_SLOWER定义不同的场景下用到的数据
在tsd.h的一开头的注释里有如下内容:

注释里清晰地表达了为了提高cache命中率,把不同场景下可能会用到的数据各自放到临近的区域,虽然TSD_DATA_SLOW、TSD_DATA_FAST、TSD_DATA_SLOWER用到的数据都定义在strcut tsd_s这个结构体里:

但是它们不同的path(fast-path或slow-path)用到的数据从定义的位置上都是连续的。
在tsd模块里的若干关键函数,如tsd_fetch_slow、tsd_state_set、tsd_add_nominal、tsd_remove_nominal等第一个入参tsd_t *tsd其实就是上图中的struct tsd_s这个结构体的指针:

上图中还有一个相关的定义是:
![]()
tsdn_s其实也是一样的struct tsd_s结构体的指针:

定义一个tsdn_t是为了和tsd_t来区分,tsdn_t是可以为NULL的,而tsd_t指针是由tls机制直接获取到的struct tsd_s数据的指针,如下图在tsd_tls.h里有定义:

而刚才说的tsd_fetch_slow、tsd_state_set、tsd_add_nominal、tsd_remove_nominal这些函数的第一个入参tsd_t *tsd实际上都是通过类似如下截图的函数tsd_get来获取到的:

所以,很显然它的地址是不可能是NULL的。相关的注释如下:

对于入参是tsdn_t *tsdn的函数如iallocztm等而言,传入TSDN_NULL和非NULL做区分可以用来表示特殊的含义,如下图:

2.2.4 详细分析一下TSD_DATA_SLOW、TSD_DATA_FAST、TSD_DATA_SLOWER这几个宏
在tsd模块的实现里,TSD_DATA_SLOW、TSD_DATA_FAST、TSD_DATA_SLOWER这三个宏可以说是关键。我们以TSD_DATA_SLOW宏为例,搜索后可以发现,它被反复的使用:

而且每次使用,它都有实际不同的含义:
我们分别来展开一下:
tsd.h里的第一处TSD_DATA_SLOW是在定义struct tsd_s这个结构体时:

TSD_DATA_SLOW展开是受#define O(n, t, nt)的影响的:

所以在struct tsd_s {的定义里,O(n, t, nt) 被定义成t TSD_MANGLE(n)
而TSD_MANGLE(n)被定义成(为了方便调试缩短了变量名,见 2.1.2 里的说明):

所以在struct tsd_s {的定义里就是声明了一下结构体里的成员,但是名字要加一个头。
再看第二处TSD_DATA_SLOW的使用,如下图,定义O是一个p_get_unsafe结尾的一个函数:

所以上面的TSD_DATA_SLOW展开就是定义了获取tsd_s结构体里的DATA_SLOW部分里的成员变量一个个的获取函数
再看第三处,和第二处差不多,是定义了获取tsd_s结构体里的DATA_SLOW部分里的成员变量一个个的带tsd的state的assert检查的函数(关于tsd的state的简要说明见 2.2.5 一节):

再看第四处,也和第二、第三处差不多,是定义了获取tsd_s结构体里的DATA_SLOW部分里的成员变量一个个的带入参检查的获取函数,因为入参是tsdn_t*,是可能是NULL的(在 2.2.3 里有说明):

第五处及以后就不一一展开了。
2.2.5 tsd的state的minimal和nominal
在这一章的最后,我们涉及一下tsd模块里的函数经常会碰到tsd的state有关的minimal和nominal的区别。
tsd里除了变量定义一块以外,还有一个tsd的state的状态维护逻辑。这篇博客先不涉及过深的状态差异上的细节,先讲提及最常见的tsd的state状态,即nominal状态。
tsd的state的定义在tsd.h里:

上图中红色框出的是最常见的nominal状态,已经初始化完了且是快速路径的是这个状态。
上面的三个状态0,1,2,到2为止,都是属于泛nominal状态,表示的是线程不处于创建和销毁时期而导致tsd“不完整”的状态。
nominal在jemalloc里的大致意思就是某种理想或者预期的分配方式,那对于内存分配,理想或者预期就是能分配效率比较快也就是快速路径的状态。
三、从tsd模块实现中可以借鉴的一些编码技巧
3.1 把不同场景高概率一起用到的数据放在一起连续的进行定义,从而增加cache的命中率
在上面 2.2.3 里讲到的tsd模块按照TSD_DATA_SLOW、TSD_DATA_FAST、TSD_DATA_SLOWER定义不同的数据块内容,虽然最终在一个结构体里,但是它们的定义是连续的,从而让数据所在的内存上的区域更容易临近,从而提到cache的命中率
3.2 通过define和undef宏定义里所依赖的宏,实现批量成员变量定义和批量函数定义
在上面的 2.2.4 一节里讲到,通过重新define和undef O(n, t, nt)来配合TSD_DATA_SLOW、TSD_DATA_FAST、TSD_DATA_SLOWER来实现批量的函数和成员变量的定义,这种宏操作可以用于简化一些重复冗余代码,方便统一化维护和修改
3.3 结构体指针的定义通过typedef不同的类型做代码直观上的轻度“解耦”,可用于体现一些入参范围的差异,如nullable和non-nullable
在 2.2.3 里我们讲到了 tsdn_t的定义是增加了NULL可能的tsd_t,我们可以借鉴这样的定义方式,来方便一些模块函数在参数传入需要做区分时的编程上的一定的解耦,方便后期的维护,也从代码里直观察觉到差异,避免参数各个场景下的误用和逻辑上的误判
相关文章:
jemalloc 5.3.0的tsd模块的源码分析
一、背景 在主流的内存库里,jemalloc作为android 5.0-android 10.0的默认分配器肯定占用了非常重要的一席之地。jemalloc的低版本和高版本之间的差异特别大,低版本的诸多网上整理的总结,无论是在概念上和还是在结构体命名上在新版本中很多都…...
【Convex Optimization Stanford】Lec3 Function
【Convex Optimization Stanford】Lec3 Function 前言凸函数的定义对凸函数在一条线上的限制增值扩充? 一阶条件二阶条件一些一阶/二阶条件的例子象集和sublevel set关于函数凸性的扩展(Jesen Inequality)保持函数凸性的操作非负加权和 & 仿射函数的…...
深入 Rollup:从入门到精通(三)Rollup CLI命令行实战
准备阶段:初始化项目 初始化项目,这里使用的是pnpm,也可以使用yarn或者npm # npm npm init -y # yarn yarn init -y # pnpm pnpm init安装rollup # npm npm install rollup -D # yarn yarn add rollup -D # pnpm pnpm install rollup -D在…...
wangEditor富文本编辑器,Laravel上传图片配置和使用
文章目录 前言步骤1. 构造好前端模版2. 搭建后端存储3. 调试 前言 由于最近写项目需要使用富文本编辑器,使用的是VUE3.0版本所以很多不兼容,实际测试以后推荐使用wangEditor 步骤 构造好前端模版搭建后端存储调试 1. 构造好前端模版 安装模版 模版安…...
chrome源码剖析—进程通信
Chrome 浏览器采用多进程架构(multi-process architecture),这种架构使得每个浏览器标签、扩展、插件、GPU 渲染等都在独立的进程中运行。为了确保不同进程之间的高效通信,Chrome 使用 进程间通信(IPC, Inter-Process …...
JJJ:linux时间子系统相关术语
文章目录 墙上时间内核管理的各种时间无时钟滴答模式(tickless mode 或 no-tick mode)简要介绍具体实现动态时钟滴答 Dynamic Ticks完全无时钟滴答(Full Tickless) nohz sleep单触发模式 oneshot mode 墙上时间 真实世界的真实时…...
0 基础学运维:解锁 K8s 云计算运维工程师成长密码
前言:作为一个过来人,我曾站在技术的门槛之外,连电脑运行内存和内存空间都傻傻分不清,完完全全的零基础。但如今,我已成长为一名资深的k8s云计算运维工程师。回顾这段历程,我深知踏上这条技术之路的艰辛与不…...
大一计算机的自学总结:位运算的应用及位图
前言 不仅异或运算有很多骚操作,位运算本身也有很多骚操作。(尤其后几个题,太逆天了) 一、2 的幂 class Solution { public:bool isPowerOfTwo(int n) {return n>0&&n(n&-n);} }; 根据二进制表示数的原理&#…...
计算机毕业设计Django+Tensorflow音乐推荐系统 机器学习 深度学习 音乐可视化 音乐爬虫 知识图谱 混合神经网络推荐算法 大数据毕设
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
AI 图片涌入百度图库
在这个信息爆炸的时代,我们习惯了通过搜索引擎来获取各种想要的信息和图片。然而,现在打开搜索引擎看到的却是许多真假难辨的信息——AI图片,这部分数据正以惊人的速度涌入百度图库,让小编不禁想问:未来打开百度图库不…...
可爱狗狗的404动画页面HTML源码
源码介绍 可爱狗狗的404动画页面HTML源码,源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果 效果预览 源码获取 可爱狗狗的404动画页面HTML源码...
【微服务与分布式实践】探索 Dubbo
核心组件 服务注册与发现原理 服务提供者启动时,会将其服务信息(如服务名、版本、所在节点的网络地址等)注册到注册中心。服务消费者则可以从注册中心发现可用的服务提供者列表,并与之通信。注册中心会存储服务的信息,…...
OpenCSG月度更新2025.1
1月的OpenCSG取得了一些亮眼的成绩 在2025年1月,OpenCSG在产品和社区方面继续取得了显著进展。产品方面,推出了AutoHub浏览器自动化助手,帮助用户提升浏览体验;CSGHub企业版功能全面升级,现已开放试用申请,…...
C++封装红黑树实现mymap和myset和模拟实现详解
文章目录 map和set的封装map和set的底层 map和set的模拟实现insertiterator实现的思路operatoroperator- -operator[ ] map和set的封装 介绍map和set的底层实现 map和set的底层 一份模版实例化出key的rb_tree和pair<k,v>的rb_tree rb_tree的Key和Value不是我们之前传统意…...
二次封装的方法
二次封装 我们开发中经常需要封装一些第三方组件,那么父组件应该怎么传值,怎么调用封装好的组件原有的属性、插槽、方法,一个个调用虽然可行,但十分麻烦,我们一起来看更简便的方法。 二次封装组件,属性怎…...
消息队列篇--通信协议篇--网络通信模型(OSI7层参考模型,TCP/IP分层模型)
一、OSI参考模型(Open Systems Interconnection Model) OSI参考模型是一个用于描述和标准化网络通信功能的七层框架。它由国际标准化组织(ISO)提出,旨在为不同的网络设备和协议提供一个通用的语言和结构,以…...
Python实现U盘数据自动拷贝
功能:当电脑上有U盘插入时,自动复制U盘内的所有内容 主要特点: 1、使用PyQt5创建图形界面,但默认隐藏 2、通过CtrlAltU组合键可以显示/隐藏界面 3、自动添加到Windows启动项 4、监控USB设备插入 5、按修改时间排序复制文件 6、静…...
汇编的使用总结
一、汇编的组成 1、汇编指令(指令集) 数据处理指令: 数据搬移指令 数据移位指令 位运算指令 算术运算指令 比较指令 跳转指令 内存读写指令 状态寄存器传送指令 异常产生指令等 2、伪指令 不是汇编指令,但是可以起到指令的作用,伪…...
DeepSeek理解概率的能力
问题: 下一个问题是概率问题。乘车时有一个人带刀子的概率是百分之一,两个人同时带刀子的概率是万分之一。有人认为如果他乘车时带上刀子,那么还有其他人带刀子的概率就是万分之一,他乘车就会安全得多。他的想法对吗?…...
AI 浪潮席卷中国年,开启科技新春新纪元
在这博主提前祝大家蛇年快乐呀!!! 随着人工智能(AI)技术的飞速发展,其影响力已经渗透到社会生活的方方面面。在中国传统节日 —— 春节期间,AI 技术也展现出了巨大的潜力,为中国年带…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
