尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TCMalloc 快速上手指南:用 Bazel 将 TCMalloc 集成为 C++ 项目的自定义内存分配器

TCMalloc 快速上手指南:用 Bazel 将 TCMalloc 集成为 C++ 项目的自定义内存分配器 TCMalloc 快速上手指南用 Bazel 将 TCMalloc 集成为 C 项目的自定义内存分配器【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo本指南以 MongoDB 仓库中内置的 TCMalloc 发行版src/third_party/tcmalloc/dist为对象完整讲解如何获取 TCMalloc 源码、跑通官方测试与 Hello World 示例并在你自己的 C 项目里通过 Bazel 的malloc属性将 TCMalloc 声明为自定义分配器。读完本文你将掌握从零搭建一个链接 TCMalloc 的 C 工程所需的全部步骤并理解WORKSPACE、http_archive、cc_binary(malloc...)等关键配置背后的原理。背景TCMalloc 是什么本文解决什么问题TCMalloc 是 Google 对 C 语言malloc()与 Coperator new的自定义实现用于替代 C/C 标准库自带的内存分配器在 Linux 上通常是 glibc 的实现。其设计目标是在高并发、大规模场景下获得更好的内存分配性能例如使用 per-CPU 缓存或 per-thread 缓存避免锁竞争、按对象大小分派到独立的 Span、缓存已释放内存以减少昂贵的系统调用等。这些细节可参考仓库内的 TCMalloc 概览。在 MongoDB 仓库中TCMalloc 以第三方代码形式内置于 src/third_party/tcmalloc/dist 目录包含完整的WORKSPACE、BUILD文件、文档docs/与测试代码tcmalloc/testing/。原版 quickstart 面向从上游仓库克隆 TCMalloc 独立使用的场景本文在此基础上同时给出在当前仓库内直接查看、构建和验证的路径。前置条件运行本教程中的代码需要满足以下条件兼容的平台如 Linux。TCMalloc 的 per-CPU 缓存依赖 Linux 内核的 restartable sequencesRSEQLinux 4.18 起合并若内核不支持 RSEQTCMalloc 会回退到 per-thread 缓存模式。更多平台信息参见 Platforms 指南。兼容的 C 编译器且至少支持 C17。大多数主流编译器均可。Git用于与 Abseil 源码仓库等外部依赖交互。安装 Git 后可通过git --version验证。Bazel4.0 或更新版本虽然你可以自由使用自己的构建系统但本指南及 TCMalloc 的大部分官方文档均假设使用 BazelTCMalloc 源码自带BUILD.bazel文件正是为此准备的。获取 TCMalloc 源码在独立使用时可以从上游仓库克隆 TCMalloc 代码# 切换到希望创建代码仓库的目录 $ cd ~ $ mkdir Source; cd Source $ git clone https://github.com/google/tcmalloc.git Cloning into tcmalloc... remote: Total 1935 (delta 1083), reused 1935 (delta 1083) Receiving objects: 100% (1935/1935), 1.06 MiB | 0 bytes/s, done. Resolving deltas: 100% (1083/1083), done. $注意如果你正在使用本 MongoDB 仓库则无需克隆——TCMalloc 源码已经完整内置在 src/third_party/tcmalloc/dist其中tcmalloc/目录即对应上游的//tcmalloc包。进入源码目录并运行全部测试$ cd tcmalloc $ bazel test //tcmalloc/... INFO: Analyzed 112 targets (12 packages loaded, 606 targets configured). ... INFO: Build completed successfully, 827 total actions $测试通过即代表 TCMalloc 安装构建环境正确。测试目标由 tcmalloc/testing/BUILD 中的create_tcmalloc_testsuite宏批量生成覆盖了tcmalloc_test、threadcachesize_test等大量单元测试与基准测试例如其中的linkstatic 1配置就是为了获得最接近真实性能的静态链接测试结果。运行 TCMalloc Hello World确认 TCMalloc 可正常构建与测试后可以编译并运行官方提供的 hello_main 示例程序观察 TCMalloc 是如何被链接进一个普通二进制文件的。首先构建tcmalloc/testing:hello_main目标tcmalloc$ bazel build tcmalloc/testing:hello_main Extracting Bazel installation... Starting local Bazel server and connecting to it... INFO: Analyzed target //tcmalloc/testing:hello_main (31 packages loaded ... ... INFO: Build completed successfully, 102 total actions PASSED in 0.1s tcmalloc$然后运行编译好的程序tcmalloc$ bazel run tcmalloc/testing:hello_main ... INFO: Found 1 target... ... INFO: Build completed successfully, 1 total action Current heap size 73728 bytes hello world! newd 1073741824 bytes at 0x14ea40000000 Current heap size 1073816576 bytes mallocd 1073741824 bytes at 0x14eac0000000 Current heap size 2147558400 bytes $这段输出非常直观地展示了 TCMalloc 的行为对应 hello_main.cc 的源码逻辑程序首先通过tcmalloc::MallocExtension::GetNumericProperty(generic.current_allocated_bytes)读取当前堆占用打印出Current heap size 73728 bytes约 72 KB 的初始堆。打印hello world!。用new char[1024*1024*1024]1 GiB分配内存并打印指针堆占用随之上升到约 1 GiB注意unique_ptr的使用是为了防止优化器把分配操作优化掉。再用malloc(1 GiB)分配一块内存堆占用进一步上升到约 2 GiB随后free()释放。输出中的 Current heap size 数值正是 TCMalloc 通过MallocExtension暴露的堆遥测信息说明程序确实运行在 TCMalloc 之上。创建并运行你自己的 TCMalloc 项目拿到 TCMalloc 源码并验证可以构建、测试、运行之后就可以在自己的项目中使用它了。推荐的做法是把 TCMalloc 源码目录保持独立通过 Bazel 的仓库机制从你自己的工程链接过去而不是把你的代码直接塞进 TCMalloc 目录。第一步创建项目目录并关联本地 TCMalloc 仓库切换到你的主开发目录新建一个开发目录若已有合适的目录可直接复用$ cd ~/Source $ mkdir TestProject; cd TestProjectBazel 通过在项目根目录放置WORKSPACE文件来链接其他 Bazel 项目。在TestProject根目录创建一个WORKSPACE文件内容如下local_repository( # TCMalloc 仓库的名称。该名称定义在你的 WORKSPACE 文件中对应其 workspace() 元数据 name com_google_tcmalloc, # 注意Bazel 路径必须是绝对路径。例如不能使用 ~/Source path /PATH_TO_SOURCE/Source/tcmalloc, )这里的name字段标识了后续在 BazelBUILD文件中引用该仓库所用的名称本例为com_google_tcmalloc。之所以叫这个名字是因为 TCMalloc 自身的 WORKSPACE 文件开头就声明了workspace(name com_google_tcmalloc)——你的local_repository名称必须与之保持一致Bazel 才能正确关联。另外务必注意path必须是绝对路径。第二步添加 Abseil 依赖TCMalloc 依赖 Abseilcom_google_absl。你可以同样用local_repository提供本地 Abseil或者官方推荐用http_archive固定到某个具体 commit# 通过 HTTP Archive 固定到特定 commit 的 Abseil # # 请到 Abseil 仓库的 commits 页面查询最新 commit。不要使用 master.zip # 因为 master 版本的 sha256 值不稳定。点开具体 commit 后 # 1. 点击 Browse Files 进入该 commit # 2. 点击 Clone or Download Code - 右键 Download ZIP 复制 HTTP Archive URL # 3. 用该 URL 填充下方 http_archive 的 urls 字段 # # 为了让 Bazel 的 http_archive 校验下载内容需要为 zip 生成 sha256。 # 在 Linux 上可用 sha256sum 命令 # # $ sha256sum github_zip_file.zip http_archive( name com_google_absl, urls [https://github.com/abseil/abseil-cpp/archive/commit_value.zip], strip_prefix abseil-cpp-commit_value, sha256 sha256_of_commit_value, )仓库内 TCMalloc 自己的 WORKSPACE 就是这样引入 Abseil 的同时还以相同方式引入了bazel_skylib、com_google_googletest、com_googlesource_code_re2、com_github_google_benchmark等依赖可作为你编写WORKSPACE时的完整参考。第三步编写你的测试代码在TestProject内创建examples目录$ cd TestProject; mkdir examples; cd examples在examples目录中创建hello_world.cc#include iostream #include cstddef int main() { std::cout Standard Alignment: alignof(std::max_align_t) \n; double *ptr (double*) malloc(sizeof(double)); std::cout Double Alignment: alignof(*ptr) \n; char *ptr2 (char*) malloc(1); std::cout Char Alignment: alignof(*ptr2) \n; void *ptr3; std::cout Sizeof void*: sizeof(ptr3) \n; return 0; }第四步创建 BUILD 文件在examples目录中创建BUILD文件cc_binary( name hello_world, srcs [hello_world.cc], malloc com_google_tcmalloc//tcmalloc, )这里的关键在于malloc关键字Bazel 的cc_binary规则原生支持通过malloc属性指定自定义分配器库。我们把malloc设置为com_google_tcmalloc//tcmalloc其中com_google_tcmalloc是WORKSPACE中定义的仓库名//tcmalloc是该仓库内提供 TCMalloc 实现的库目标对应仓库内 tcmalloc/BUILD 中name tcmalloc的那个cc_library。关于 Bazel BUILD 文件的更多写法可查阅 Bazel 官方的 C 教程。另外仓库自身的大量测试目标也广泛使用了这一机制——例如 tcmalloc/BUILD 等多处都出现了malloc //tcmalloc的写法这说明malloc属性正是 TCMalloc 官方推荐的接入方式。第五步构建并运行回到工作区根目录构建目标通常建议从工作区根目录构建# 从工作区根目录构建 $ cd ~/Source/TestProject Source/TestProject$ bazel build //examples:hello_world --cxxopt-stdc17 INFO: Analysed target //examples:hello_world (12 packages loaded). INFO: Found 1 target... Target //examples:hello_world up-to-date: bazel-bin/examples/hello_world INFO: Elapsed time: 0.180s, Critical Path: 0.00s INFO: Build completed successfully, 1 total action Source/TestProject$ bazel run //examples:hello_world INFO: Running command line: bazel-bin/examples/hello_world Standard Alignment: 16 Double Alignment: 8 Char Alignment: 1 Sizeof void*: 8 Source/TestProject$可以看到程序成功打印出各类型的对齐信息与指针大小且二进制已经通过malloc属性链接了 TCMalloc。第六步固化 C17 编译选项上一步我们在命令行传入了--cxxopt-stdc17。为了避免每次构建都手写该参数可以把它写进项目根目录的.bazelrc文件build --cxxopt-stdc17之后直接执行bazel build //examples:hello_world即可。至此你已经创建了第一个使用 TCMalloc 的二进制程序。下一步学习路径阅读 TCMalloc 概览涵盖内存分配的基本概念与使用 TCMalloc 的最佳实践。通读 TCMalloc 参考文档了解malloc()、::operator new以及 TCMalloc 中其他分配/释放例程的具体行为。查阅 TCMalloc 的 C 头文件 tcmalloc/malloc_extension.h其中包含 TCMalloc 支持的扩展能力如本文 Hello World 用到的GetNumericProperty堆遥测接口。若有意向 TCMalloc 提交代码请先阅读 贡献指南。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表