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 技术也展现出了巨大的潜力,为中国年带…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...

C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...

DAY 45 超大力王爱学Python
来自超大力王的友情提示:在用tensordoard的时候一定一定要用绝对位置,例如:tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾: tensorboard的发展历史和原理tens…...
C++ 类基础:封装、继承、多态与多线程模板实现
前言 C 是一门强大的面向对象编程语言,而类(Class)作为其核心特性之一,是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性,包括封装、继承和多态,同时讨论类中的权限控制,并展示如何使用类…...

CMS内容管理系统的设计与实现:多站点模式的实现
在一套内容管理系统中,其实有很多站点,比如企业门户网站,产品手册,知识帮助手册等,因此会需要多个站点,甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...