2KB项目,专业的源码交易网站 帮助 收藏 每日签到

Nah Lock: 一个无锁的内存分配器

  • 时间:2019-01-23 18:33 编辑:2KB 来源:2KB.COM 阅读:398
  • 扫一扫,手机访问
  • 分享
摘要:
TCMalloc 英文原文:Nah Lock: A Lock-Free Memory Allocator

概述

我实现了两个完全无锁的内存分配器:_nalloc 和 nalloc。  我用benchmark工具对它们进行了一组综合性测试,并比较了它们的指标值。

与libc(glibc malloc)相比,第一个分配器测试结果很差,但是我从中学到了很多东西,然后我实现了第二个无锁分配器,随着核数增加至30,测试结果线性提高。核数增加至60,测试结果次线性提高,但是仅比tcmalloc好一点。

想要安装,输入命令: git clone ~apodolsk/repo/nalloc,阅读 README文档。

背景

内存分配器很重要,因为大多数的程序在使用它们,并且许多程序在大量使用它们.对于数以亿计的好程序而言,一个糟糕的分配器会是竞争的中心点;而一个好的分配器会是天上掉下来的替代品,用来扭转不良内存访问的程序为硬件友好的模式.

我所知道的所有可伸缩的内存分配器,包括已存的无锁分配器,通过拆分地址空间到CPU或线程局部子堆(subheap),尝试将分配过程转化为数据并行问题.在最好的情况下,这会带来优化位置,减少错误共享和增强预处理的额外好处,因为每个线程访问的是线程私有的高速缓存线的邻近集合.

在最糟糕的情况下,使用内存分配器的程序和分配器地址不是并行的,这样就需要小心的设计,来减少导致内存块在线程间转移的人为的或者显式的通讯.再者,能实现这点的可用选项,与减少内存碎片和内存崩溃的需要相冲突.

这里无需描述时序算法.

tcmalloc 和 jemalloc 是性能最好的通用分配器中的两个.ptmalloc是glibc默认的分配器.

分析和挑战

对于这个项目是否和这个教程相匹配,我有些信心不足.所以,对这个问题,我尝试着提出一些非明显的分析.我在介绍里面提到过,所以你可以略过此处.

我提到的"可变的数据并行"是一个新的难点.内存分配与我们在类中所见的所有问题形成鲜明的对比,因为它们可以在工作前做并行性分析.例如,在渲染器中,你可以通过这种方式处理,即允许数据不经过通讯来处理,这样来达到工作和间隔之间程序化的平衡.

另一方面,一个并行分配器,由未知的依赖提供预分配的,模糊的工作负载,这些依赖需要未知数量的通讯来解决.由此,它需要扩大到不同的并行级别.

实际上,这意味着我不得不花费大量的时间考虑,"如果工作负载强制线程大量通讯,这个选择有意义吗?是有意义还是无意义呢?".(在这个方向,我没有任何收获.根据我的测试,这些分配器的状态,看起来和最糟糕的情况一样.)

令我非常兴奋的是,一些设计问题与web服务器非常类似。像服务器一样,在突发的工作负载中,分配器需要满足延迟和吞吐量的目标,这通过平衡资源使用的目标,如减少碎片和崩溃,来实现。“分配更多的节点,甚至比需要的还多”和“从全局堆中获取更多的页,甚至比需要的还多”有类似的开销和益处,随着而来“启动开销”的问题也会浮现。

接下来提到的,是我碰到的没那么抽象的困难点。

__nalloc

介绍

__nalloc是"幼稚的",因为它可能是,我每个学期开始会用到的,几乎同样的基础设计.我假定瓶颈在同步控制,于是,我计划将一个快速的单线程算法,加入到高效的无锁包装器.如果我更倾向于分析,而不是"做听起来很优雅的事情",或者使用已存的只有大概轮廓的分配器,我所遇到的一些神奇的,暂未命名的问题,可能从一开始就会变得非常明显.

__nalloc和nalloc的目标分配大小均小于或等于1024B,这主要是为了让任务更简单.

主要想法如下:

  • 在每个局部线程子堆栈运行单线程的分配器 ("附有范围标识,以及易于分开与合并的隔离链表,").

  • 使用页的无锁栈,从全局堆中分配内存.

  • 使用属于每个线程的另一个无锁栈,返回移动的/"善变的"块到它们的原始线程.

下面是一个更详细的算法.如果觉得上面附加的描述言之有理,你可以略过此处.不管怎样,在最后提到的关于"善变的块"的部分很有意思:

  • 每个线程保留着各种大小空闲内存块的"空闲双链表".

  • 每个块位于一个"arena",它是一个页大小的块内存,地址和大小"自然对齐".线程通过从页的全局无锁栈获取更多的arena来填充空闲链表.

    • 当页的栈为空的时候,线程通过调用mmap()从OS分配一批页获取arena.

    • 每个新的arena保留这最大空间的单一块.

  • 在malloc()之中, 一个线程从栈里取出一个匹配请求大小的块.

    • 它削去多余的空间使之成为新的块.

    • 如果链表为空,它会尝试下一个最大的块,直到分配完毕而不得不获取新的arena.

  • 在free()之中, 一个线程会尽可能的合并空闲块及其相邻的部分.

    • 为了实现这点,每个块B需要4B的头信息来存储内存中"is_free"标识,B大小以及B的"左边"的大小.

    • 所有合并的相邻的空间都会从它们的空闲链表中删除.

  • 如果线程F释放了线程M分配的块B,线程F会将块B插入到一个与线程M相关联的"善变的"无锁栈.

    • 当线程M用完了块,它会在单个cmpxchg操作中取出整个栈的内容,然后将每个块加入到它的空闲链表.

    • 线程F如何找到这个链表呢?每个arena保留了线程"善变的"块的栈的指针.因为arena是自然对齐的,线程F可以根据线程B的地址计算线程B的arena的地址.

    • 每个arena也有一个内部栈存储"非自身占有的块".如果由于线程退出,而不得不在所有的块空闲之前释放arena,它会修改arena的"善变的块"指针指向"非自身占有的块"的栈.

基准测试

我编写了三个基准测试程序,分别在perf, gperftools和vtune中进行分析.

第一个测试中,每个线程随机分配,写入和释放到一个私有的内存池.

第二个测试中,线程分配到一个全局池,它实现为一个无锁栈的集合.

第三个测试中,单线程分配到一个全局池,其他线程从全局池中释放内存.

全部的工作负载在线程间保持恒定.分配大小限制到小于等于1024B.每个线程最大分配字节数受到限制,但是线程在释放旧的分配空间后,可以申请新的空间.全局时间使用gettimeofday()来计算时间间隔.

除了提到的,即将展示的图表是由第一个测试生成的.

按时间先后顺序优化和评论

我需要经常上下对齐地址.令人吃惊的是,我的align_up()和align_down()函数,只是一个增加或减少,以及取模运算,竟然占用了9%的运行时间.我为2的平方使用bitops来替换这些函数,之前的开销完全消除.它没有提升规模,但是我惊奇的发现,某种方式的算术中,div竟有如此大的差别.

我最初的设计是使用"arena"双链表而不是块.我原以为它会增加碎片和位置,以及,按顺序预取会耗尽arena.取而代之的是,线程通过O(n)的时间复杂度搜索成千上万个"不是足够满"的arena,寻找一个足够的连续空闲空间,来满足大的分配请求.像旋转arena之类的技巧起不到作用.

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部