测试内存分配器:ptmalloc2 vs tcmalloc vs hoard vs jemalloc,同时尝试模拟真实世界的负载

本文主要是介绍测试内存分配器:ptmalloc2 vs tcmalloc vs hoard vs jemalloc,同时尝试模拟真实世界的负载,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文

当我们为参与者开发自己的分配器时,我们需要对其进行测试,更重要的是,需要将其与现有的分配器进行基准测试。显然,虽然有相当多的分配器测试程序,但这些测试中的大多数并不能代表真实世界的负载,因此,只能提供关于真实程序中分配器性能的非常粗略的想法。
例如,相当流行的t-test1.c(参见,例如,t-test1.c)经常被用作单元测试,因此它必须测试诸如calloc()realloc() 之类的东西;另一方面,这些功能在现代C++程序中很少使用,因此对它们进行基准测试实际上会对测试的真实世界条件(至少对于C++)跑偏。此外,均匀随机分布(分配的空间大小和创建/销毁项目的相对频率)虽然最容易模拟,但不能代表真实世界的情况(事实上,已知在大块内存上均匀随机分布是有效禁用缓存的最佳方式,但幸运的是,这在现实世界中很少发生。)最后但并非最不重要的一点是,只测试分配/取消分配,而不访问分配的内存,这是不现实的。

目标和要求

因此,我们决定开发自己的测试,尝试解决上面列出的所有问题。我们的目标是开发一个分配器的测试程序。一方面它将尽可能接近真实世界的C++程序,但是另一方面,它将允许测量存在的性能差异(没有退化成“所有分配器性能都是一样的,因为我们的测试在非分配代码中花费了太多的时间”)。当尝试进一步深入实际时,它产生了以下一组要求:

  • 我们希望我们的测试只使用new/delete(或malloc/free)。其他的东西在C++中很少使用,对它们做基准测试与“接近真实世界的C++程序”的目标不太一致。
    • 或者从另一个角度来看也是一样:如果你的程序真的需要很多realloc()调用,那么要为你的应用找到最佳的分配器,你需要一个不同的基准——很可能是一个不同的分配器。
  • 我们希望我们的测试使用一些真实场景的统计分布——既用于分配条目的大小的分布,也用于“分配条目的使用周期”的分布。
    • 我们确实意识到这必然是一个非常粗略的近似,但对于绝大多数程序来说,任何诚实的尝试都会比使用耗尽缓存(cache-killing)的一致分布更好。
  • 我们希望我们的测试能够访问所有分配的内存:我们应该至少写一次分配的内存,并且至少读回一次。
    • 我们承认,可以有不止一次的写/读操作,但我们认为至少一次是绝对最低限度的,目的是至少接近现实场景。

基本思想

从30000英尺的高度看,我们测试的一个非常基本的想法与t-test1.c非常相似:

  • 我们有一堆“槽(slot)”,每个“槽”代表一个潜在的分配项目。
  • 在每次迭代中,我们都会在槽上随机选择:
    • 如果它是空的(empty)–我们正在分配一个项目并将其放入槽中
    • 如果是释放的(free),我们将释放该项目,并将槽清空
  • 如果有多个线程,则每个线程都沿着上面的线独立工作。

差不多就是这样。

使分布更现实

在这个模型中,有两种不同的分布:(a)分配项目的大小分布,和(b)槽(slot)内随机选择的分布(这转化为分配项目的相对寿命分布)。
对于分配大小的分布,我们认为

p ~ 1/size

(其中“~”表示“成比例”且 size <= max_size)足以表示某种或多或少现实的场景。换句话说,分配 30字节的概率是分配 60字节概率的两倍,而分配60字节的概率又是分配 120字节概率的两倍,依此类推。在实践中,为了确保计算足够快,我们必须使用分段线性函数的近似值,但我们希望不要偏离1/size的目标太多。

对于选择用于操纵的槽(slot)分布,我们进行了大量实验,以确保一方面,访问频率较低的槽的概率快速降低(特别是,大大快于1/x,这太慢了,无法代表我们职业生涯中看到的真实世界分布),但另一方面,它们不会太快地达到虚拟零。最后,我们不得不求助于所谓的帕累托分布(在其经典版本中,“20%的人喝80%的啤酒”规则)。除了对于许多不同的非计算机相关的真实世界分布是一个非常好的近似值外,帕累托分布确实提供了我们认为相当接近真实世界的访问频率近似值;另外,我们观察到,在某些参数(槽数量和max_size)下,它与访问L1的次数 ~= 访问L2的次数 ~= 访问L3的次数 ~= 访问主RAM的次数没有太大的偏差,我们认为这是一个好迹象。

与分配的大小一样,我们确实需要使用分段线性函数,以便在测试时使事情变得合理快速,但我们确实希望这不是太糟糕。

访问分配的内存

如上所述,我们确实希望访问分配的内存;在我们的测试中,我们在分配之后编写整个块(使用memset())——并在释放之前读取它。这是我们在现实世界中所能想象的最低访问量(嘿,如果我们真的想分配,也许我们真的想在那里写点什么?)。

补偿机制

我们所做的所有工作(特别是关于分布区间)都在CPU方面取得了巨大的成功(尽管我们尽可能地避免了间接操作,但寄存器内(in-register)计算仍然非常重要。此外,我们确实想减去memset()和内存读取的理想情况成本,以便更清楚地看到不同分配器之间的差异。
为此,我们总是运行两个测试:(a)被测分配器,(b)虚拟“void”分配器(它完全不做任何事情),然后我们从(a)中花费的时间中减去(b)中花费的时间,以获得被测分配器的性能,而无需测试本身的成本。

测试

测试程序。我们运行我们的测试应用程序,按照上面讨论的思路设计,并在Github.alloc test上提供。
系统。我们在一个典型的2槽高的服务器上运行我们的所有测试,有两个E5645,每个E5645都有6个内核,具有超线程(Hyperthreading)(~=“2路SMT”),12M的三级缓存,能够以2.4GHz(涡轮模式下为2.67Ghz)的频率运行。这个主机有32G的RAM。OS:Debian 9“Stretch”(截至本文撰写之时,它是当前Debian“稳定的”版本)。非常简单:这是一个非常典型的(即使有点过时)现实世界的“主力”服务器。
正在测试的分配器。我们的第一组测试是在4个流行的Linux分配器上运行的:

  • 内置glibc分配器(一般认为它是经过大量修改的ptmalloc2)
  • [hoard]. 下载https://github.com/emeryberger/Hoard并编译
  • tcmalloc. apt-get install google-perftools
  • jemalloc. apt-get install libjemalloc-dev, apt-get install libjemalloc1

对于所有的分配器,我们没有使用LD_PRELOAD;相反,我们(对于除内置分配器外的所有分配器):

  • 编译我们的测试应用程序时使用了-fno-builtin-malloc-fno-builtin-calloc-fno-builtin-realloc-fno-builtin-free标志(如perftools.README中所建议的那样);在没有这些标志的情况下,我们至少在tcmalloc中遇到了问题。
  • 将我们的测试应用程序链接到-lhoard-ltcmalloc-ljemalloc

测试设置。为了运行我们的测试,我们必须选择某些参数,最重要的是——我们要分配的RAM数量。为了使我们的测试尽可能接近真实世界,我们决定使用大约1G的分配RAM进行测试;事实上,由于我们尽可能使用2的幂,我们最终得到了1.3G的分配RAM。需要注意的是,在处理多个线程时,我们决定在所有线程上拆分相同数量的RAM(这很重要,因为我们确实希望排除由于与CPU缓存相比使用的RAM大小不同而产生的差异)。
另一个参数(因测试而异)是线程数。由于我们的机器支持24个线程(2个CPU6个内核/CPU2个线程/内核=24个线程),我们使用1-23个线程运行测试(保留最后一个硬件线程以满足操作系统自身的需要,因此中断和后台活动不会对我们的测试产生太大影响)。

测量什么。对于第一组测试,我们只测试了两个参数:

  • 测试运行时花费的时间。然后,我们使用这些数据来推导“每个malloc()/free() 对的CPU时钟周期”指标。
  • 程序的内存使用情况(以RSS=“Resident Set Size”的最大值来衡量);在没有交换的情况下,它是程序从操作系统分配的RAM量的一个合理的良好指标。我们使用这些数据来计算“内存开销”(作为“从操作系统分配的RAM数量”与“应用程序级别通过malloc()请求的RAM数量”的比率)。

测试结果

现在,根据测试条件的描述(如果有什么遗漏,请给我们一个提示,我们很乐意进一步阐述),我们可以进入本文最有趣的部分——测试结果。
以下是我们得到的数据:
malloc test result
这或多或少与之前的测试和我们的预期一致,但是:

  • 由于我们试图模拟真实世界的行为,现代最先进的 Malloc 库之间的性能差异并不像有时所说的那样明显。特别是,tcmalloc 和 ptmalloc2 之间观察到的最大性能差异是,tcmalloc 的性能比 ptmalloc2 好1.7倍,尽管它只发生在一个线程上,并且在线程数量较多的情况下,ptmalloc2 的性能比 tcmalloc 好(高达1.2倍)
  • 值得注意的是,那些使用较少线程( tcmalloc 和 hoard)表现较好的 malloc,在大约12个线程时开始表现逐渐较差,到了大约18-20个线程时,表现比 ptmalloc2 和 jemalloc 差。
    • 这是否与超线程(SMT)有关(在本框中,12个线程是SMT发挥作用的一个点),还是与其他任何东西有关,都有待进一步研究。特别值得一提的是,如果我们在不同的进程中运行我们的测试线程,那么找出这种影响是否持续是很有趣的。

malloc overhead
我们测量的第二个参数是“内存开销”。这对于整体性能非常重要,因为我们产生的RAM开销越少,缓存的效率就越高。为了粗略估计这种影响,我们可以考虑分配程序的开销为2倍,实际上浪费了每一级缓存的一半,因此,相对于12M的三级缓存,使用这样一个开销为2倍的分配程序,我们将认为“好像”只有6M。
正如我们所看到的,从这个角度来看,jemalloc 是一个明显的赢家,ptmalloc2 是第二好的,而 tcmalloc 在任何线程数 > 1 的情况下都远远落后。

结论

现在,是时候从这些数据中得出我们主观的初步结论了:

  • 在不指定具体应用程序的情况下,我们测试的现代 malloc 库彼此之间性能差距不大,更重要的是,在某些条件下,它们中的每一个都可以超越另一个。换句话说:如果你确实想通过改变 malloc 获得性能,请确保用你自己的应用程序测试它们。
  • 如果我们不知道确切的应用程序(比如为OS发行版选择默认malloc),那么我们的建议如下:
    • 考虑到奇怪的内存开销,我们应该远离 hoard
    • tcmalloc 和 ptmalloc2 非常相似(在这一点上,tcmalloc 对于台式机和 ptmalloc2——对于服务器来说似乎有优势,但现在做出任何明确的结论还为时过早)
    • 但在我们的拙见中,到目前为止,总冠军是 jemalloc。它的低内存开销有望改善缓存的处理方式,对于“普通应用程序”,我们希望它的性能至少不会比其他应用程序差。你的情况可能会有所不同,上面的测试不包括电池消耗,请参阅上面的“确保使用您自己的应用程序测试电池”部分。

这篇关于测试内存分配器:ptmalloc2 vs tcmalloc vs hoard vs jemalloc,同时尝试模拟真实世界的负载的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/723669

相关文章

关于Java内存访问重排序的研究

《关于Java内存访问重排序的研究》文章主要介绍了重排序现象及其在多线程编程中的影响,包括内存可见性问题和Java内存模型中对重排序的规则... 目录什么是重排序重排序图解重排序实验as-if-serial语义内存访问重排序与内存可见性内存访问重排序与Java内存模型重排序示意表内存屏障内存屏障示意表Int

如何测试计算机的内存是否存在问题? 判断电脑内存故障的多种方法

《如何测试计算机的内存是否存在问题?判断电脑内存故障的多种方法》内存是电脑中非常重要的组件之一,如果内存出现故障,可能会导致电脑出现各种问题,如蓝屏、死机、程序崩溃等,如何判断内存是否出现故障呢?下... 如果你的电脑是崩溃、冻结还是不稳定,那么它的内存可能有问题。要进行检查,你可以使用Windows 11

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测