技术与生活 性能之重 持久内存编程指南—乱译连载 (5. PMDK介绍)

持久内存编程指南—乱译连载 (5. PMDK介绍)

本章给出了PMDK中可用的库和工具简要列表,以及简要说明了什么时候使用它们。现在你应该知道使用这些库和工具能干什么了。后面的章节,将介绍如何使用这些库和工具开发软件。

5  PMDK介绍

前面章节介绍了持久内存的特性,为此新颖的技术写软件是复杂的。研究过或者开发过代码的人都证明了此点。为了使此项工作更容易,Intel创建了PMDK。PMDK开发团队设想使之成为标准库,以提供应对持久内存编程的常见挑战。

5.1  背景

PMDK已成为众多开源库和工具的一分子,致力于简化管理员对持久内存设备的管理,以及应用开发者对持久内存设备的访问。PMDK库基于SNIA NVM编程模型构建。PMDK提供各种层次的API,一些简单,一些很复杂。使用者需要据自己的实际场景来决定使用哪个层次的API。PMDK由Intel创建并且支持自己的硬件产品,Intel也承诺确保库和工具对于厂商和平台是中立的。这就是说PMDK不绑死Intel 处理器或者其持久内存设备,也欢迎个人、硬件开发商、ISV等贡献代码。

PMDK是BSD 3-Clause License,允许开发者嵌入到任何软件,不管是开源软件,还是专属软件。这允许你挑选PMDK的个别部件,只集成所需的代码。PMDK在GitHub (https://github.com/pmem/pmdk)上免费提供,并有专门的网站点:https://pmem.io,在每个库的自己的页面都有手册。

本书附录B 描述了如何安装到系统。活动社区在Google论坛上,https://groups.google.com/forum/#!forum/pmem,开发者、系统管理员以及其他对持久内存感兴趣的人可以在上面提出问题寻求帮助。这里有很多资源。

5.2  选择正确的语义

PMDK中有很多库,需要仔细挑选他们。PMDK提供两大类库:

  1. 易失性库,用于只想利用持久内存容量的场景;
  2. 持久性库,希望在软件中实现失效-安全性持久内存算法的场景。

在决定如何更好的解决问题时,仔细考虑是上面哪种场景。失效-安全持久程序面对的挑战与易失性的程序明显不同。预先选择正确的方法将极小化重写代码的风险。当然也可以为应用的不同部分选择不同的类别,这取决于功能特性需求。

5.3  易失性库

易失性库使用起来通常比较简单,因为当持久内存无效时,程序会转而依赖DRAM内存。这提供了更为简单直接的实现。取决于负载,综合来看该库与类似持久库来比,花费更少的开支,因为不需要确保失效时的数据一致性。

本节介绍在应用里易失性场景下所使用的库,包括该库是什么,以及什么时候使用它。这些库可能会有重叠使用场景。

5.3.1 libmemkind

该库是什么?

该库之所以叫libmemkind,是建立在jemalloc之上的一个用户可扩展的堆管理器。它能够控制内存特性和不同种类内存之间堆的分区。各类内存由操作系统内存策略来定义,这些内存策略应用于虚拟地址范围。内存特性由Memkind来支持,除了控制NUMA和页尺寸大小外。Jemalloc非标准接口已经被扩展,可以通过memkind的分区接口向操作系统虚拟内存发出请求。也可以通过其他memkind接口(在启用该特性时)控制、扩展内存分区特性并分配内存。可以使用Memkind接口创建并控制背后是文件支撑的PMEM类型持久内存。第10章有更详细的介绍。你可以下载memkind并阅读架构文档和API文件,地址:http://memkind.github.io/memkind/ 。memkind的开源工程在:https://github.com/memkind/memkind

什么时候使用?

当你在易失性应用中,想手动移动所选内存对象到持久内存内存时,维持传统的编程模型。Memkind库提供类似malloc() and free()的语义。在使用持久内存当作易失性内存时,我们推荐使用此内存分配器。

现代内存分配器通常依赖于匿名内存映射来从操作系统提供内存页。对于大多数系统来说,这意味着只有在第一次访问页面时才分配实际的物理内存,从而允许操作系统过度配置虚拟内存。此外,如果需要,可以调出匿名内存。当对基于文件的类型(如PMEM-kind)使用memkind时,物理空间仍然只在第一次访问页面时分配,其他描述的技术不再适用。当没有可用的内存可供分配时,内存分配将失败,因此在应用程序中处理此类失败非常重要。

所描述的技术在隐藏手动动态内存分配(如碎片)固有的低效性方面也发挥了重要作用,这种低效性会在没有足够的连续可用空间时导致分配失败。因此,对于具有不规则分配/释放模式的应用程序,基于文件的类型可以显示出低空间利用率。使用libvmemcache可以更好地处理此类工作负载。

5.3.2 libvmemcache

该库是什么?

libvmemcache是一种可嵌入的轻量级内存缓存解决方案,它通过高效和可扩展的内存映射,充分利用了大容量内存,如具有直接内存访问(DAX)的持久内存。libvmemcache具有独特的特性:

  1. 基于扩展的内存分配器避免了影响大多数内存中数据库的碎片问题,并允许缓存为大多数工作负载实现非常高的空间利用率;
  2. 缓冲最小最近使用(LRU)算法将传统的LRU双链表与非阻塞环形缓冲区结合起来,在现代多核CPU上提供高度的可扩展性;
  3. critnib索引结构提供高性能,同时非常节省空间。

cache针对相对大型尺寸场景优化。最小尺寸可能256字节,但如果超过1k时, libvmemcache 工作得很好。

第10章描述了该库的更多细节。Libvmemcache的开源工程代码见:https://github.com/pmem/vmemcache.

什么时候使用?

对于你的工作负载情况,当使用系统正常的内存分配方案空间利用率低时,使用libvmemcache。

5.4  持久性库

持久性库帮助应用在失效时保持数据结构一致性。与前面描述的易失性库相比,该库提供新的语义,并充分发挥持久内存的独特能力。

5.4.1 libpmem

该库是什么?

libpmem 是一个低层 C库,提供了操作系统原语之上的基本抽象。它自动检测平台上可用的特性,并选择正确的持久语义和为持久内存优化的内存传递方法(memcpy)。大多数应用至少需要此库的一部分特性。

第4章描述了应用使用持久内存的需求,第6章则进行了更为深入的介绍。

什么时候使用?

当需要修改已存在的使用内存映射IO的应用时。该应用可以利用持久内存的同步原语,如:用户空间flush,来代替msync(),这可以减少kernel调用开支。

当你想从头建立时也可以使用该库。它能够支持低层持久数据结构的实现,可自定义内存管理机制和恢复逻辑。

5.4.2 libpmemobj

该库是什么?

libpmemobj 是一个C库,提供事务性的对象存储能力,带有手工动态内存分配器、事务,和持久内存编程通用设施。该库解决了许多持久内存编程中常见的算法和数据结构问题。第7章给出了该库的详细描述。

什么时候使用?

编程语言为C,并且需要数据结构设计的灵活性,而不是使用通用的内存分配器和通用事务处理。

5.4.3 libpmemobj-cpp

该库是什么?

libpmemobj-cpp也称为libpmemobj++,是一个C++头文件库,只有头文件,使用C++的元编程特性来提供一个比libpmemobj更简单的、更不易出错的接口。通过使用许多C++程序员熟悉的概念,如:智能指针、基于闭包的事务(closure-based transactions)等,来加快持久内存应用的开发。该库也提供了定制的、STL兼容的数据结构和容器,所以应用开发者不需要为持久内存重新发明基本的算法。

什么时候使用?

当C++可选时,更倾向于libpmemobj-cpp,而不是 libpmemobj。第7章描述了该库的细节。

5.4.4 libpmemkv

该库是什么?

Libpmemkv 是一个针对持久内存优化过的通用嵌入式本地KV库。该库易于使用并可在多种不同语言间集成,包括:C、C++,以及JavaScript。

该库为不同存储引擎提供一个可插拔的后端。尽管它的设计初衷是用来支持持久化场景的,它依然可以被当作易失性库来使用。

第9章描述了该库的细节。

什么时候使用?

刚开始进行持久内存编程时,建议使用该库,因为该库易于理解,并且接口简单。当不需要复杂定制数据结构并且通用KV存储接口足够解决当前问题时,推荐使用。

5.4.5 libpmemlog

该库是什么?

Libpmemlog是一个C库,实现了持久内存追加方式写日志文件,并且其操作在电源失效时是安全的。

什么时候使用?

当你的场景与所提供的日志API相吻合时;否则使用通用库会更好,如:libpmemobj 或者libpmemobj-cpp。

5.4.6 libpmemblk

该库是什么?

Libpmemblk是一个C库,用来管理固定尺寸的块。通过基于缓存的函数,它提供了失效-安全的接口来更新块。

什么时候使用?

当需要简单的定长块数组时,并且不需要直接以字节粒度访问块的场景下。

5.5  工具和命令行

PMDK提供了各种工具和设施来帮助开发和部署基于持久内存的应用。

5.5.1 pmempool

该工具是什么?

Pmempool 工具用来管理和离线分析持久内存池。它的各种工具,可用于应用的整个生命周期。包括:

  1. 从内存池中获取信息及统计数据;
  2. 检测内存池的一致性,并且在可能情况下修复它;
  3. 创建内存池;
  4. 移除/删除已创建的内存池;
  5. 更新内部元数据到最新布局版本;
  6. 在池集中同步复制;
  7. 修改池子内部的数据结构;
  8. 启动或者禁用池子和池集特性。

什么时候使用?

当你使用任一PMDK持久库为应用创建持久内存池的时候。

5.5.2 pmemcheck

该库是什么?

Pmemcheck工具是一个基于Valgrind的工具,用于在应用动态运行时分析常见的持久内存错误,如:遗漏flush或者事务的错误使用。第12章详细介绍了该工具。

什么时候使用?

当使用libpmemobj、libpmemobj-cpp,或者libpmem开发应用时,该工具很有用处,它可以帮助你找到那些持久化应用中常见的bug。我们建议在代码的开发早期就运行错误检测工具,以避免形成一堆难以调试的问题。PMDK开发者把pmemcheck测试集成进了PMDK的持续集成中,我们建议所有持久应用,也这样做。

5.5.3 pmreorder

该库是什么?

Pmreorder工具帮助检测持久化应用中失效时的数据结构一致性问题。它首先录制然后再回放应用中的持久化状态,以验证应用在任一可能中间状态时的数据结构的一致性。第12章将详细介绍该工具。

什么时候使用?

就像pmemcheck一样,pmreorder 是一个查找难以调试的持久化问题所必需的工作,并且任一持久内存应用都应当把它集成进开发和测试周期。

5.6  本章小结

本章给出了PMDK中可用的库和工具简要列表,以及简要说明了什么时候使用它们。现在你应该知道使用这些库和工具能干什么了。后面的章节,将介绍如何使用这些库和工具开发软件。

下一章介绍libpmem,并描述如何使用它创建一个简单的持久化应用。

作者: charlie_chen

编程是一生最爱: >> 架构与设计; >> 软件工程; >> 项目管理; >> 产品研发。
联系我们

联系我们

022-XXXXXXXX

在线咨询: QQ交谈

邮箱: 1549889473@qq.com

欢迎交流。
关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部