查看原文
其他

针对持久内存的应用程序开发

常华Andy Andy730 2024-03-16

Source: Persistent Memory Development Kit


在过去的几十年中,计算机系统已经实现了如图1所示的内存存储层次结构。内存存储层次结构利用局部性原则,使经常访问的数据最接近 CPU。连续几代技术对缓存的数量、大小和速度进行了迭代,以确保 CPU 能够访问最常用的数据。CPU速度持续变快,每一代新的CPU都会添加更多内核和线程,因为它们试图维持摩尔定律。易失性 DRAM 和非易失性存储(如 NAND SSD 或硬盘驱动器)的容量、价格和速度没有跟上步伐,并迅速成为系统和应用性能的瓶颈。


图1:内存存储层次结构


持久内存 (PMEM),也称为非易失性内存 (NVM) 或存储类内存 (SCM),在图 2 所示的内存存储层次结构中提供了一个新条目,填补了性能/容量差距。


图 2:具有持久内存层的内存存储层次结构


使用持久内存时,应用程序具有可用于数据放置的新层。除了内存层和存储层之外,持久内存层还提供比 DRAM 更大的容量,并且性能明显快于存储。应用程序可以访问持久内存,就像使用传统内存一样,无需在内存和存储之间来回分页数据块。



这对应用程序开发人员意味着什么?


持久内存层的引入为应用程序开发人员提供了放置数据和数据结构的位置的选择。传统上,数据被读取和写入易失性内存,并刷新到非易失性持久性存储。启动应用程序时,必须先将数据从存储读取到易失性内存中,然后才能访问它。根据工作数据集的大小,这可能需要几秒钟、几分钟或几小时。借助巧妙的应用程序设计,开发人员和应用程序架构师现在可以利用这项新技术来提高性能并缩短应用程序启动时间。


持久内存引入了一些新的编程问题,这些问题不适用于传统的易失性内存。这些包括:


数据持久性:

  • 在刷新之前,不能保证存储区是持久的。尽管对于已有数十年历史的内存映射文件 API(如 Linux 上的 mmap() 和 msync())也是如此,但许多程序员尚未处理刷新内存持久性的需要。遵循标准 API(如 msync() 将更改刷新到持久性)将按预期工作。但是,也可以进行更优化的刷新,即应用程序直接从 CPU 缓存中刷新存储,而不是调用内核。

  • CPU 具有无序 CPU 执行和缓存访问/刷新。这意味着,如果应用程序存储了两个值,则它们成为持久性的顺序可能不是应用程序编写它们的顺序。


数据一致性:

  • 在 x86 体系结构中,8 字节存储是 powerfail atomic —— 如果在与 PMEM 对齐的 8 字节存储期间发生电源故障,则在重新启动后,将在该位置找到旧的 8 字节或新的 8 字节(不是两者的组合)。 

  • x86 上任何大于 8 字节的内容都不是 powerfail atomic,因此由软件来实现一致性所需的任何事务/日志记录/恢复。 请注意,这是特定于 x86 的 —— 其他硬件平台可能具有不同的原子大小(PMDK 的设计使得使用它的应用程序不必担心这些细节)。


内存泄漏:

  • 持久性存储的内存泄漏是持久性的。重新启动服务器不会更改设备上的内容。在当前易失性模型中,如果应用程序泄漏内存,重新启动应用程序或系统将释放该内存。


字节级访问:

  • 应用程序开发人员可以根据应用程序要求在字节级别进行读取和写入。读/写不再需要对齐或等于存储块边界,例如:512字节,4KiB或8KiB。存储不需要读取整个块来修改几个字节,然后将整个块写回持久性存储。应用程序可以根据需要自由读取/写入尽可能多的或尽可能少的读/写。这可以提高性能并减少内存占用开销。


错误处理:

  • 应用程序可能需要直接检测和处理硬件错误。由于应用程序可以直接访问持久内存介质,因此任何错误都将作为内存错误返回到应用程序。 


应用程序开发人员可以使用传统的系统调用(如 memcpy()、memmap() 等)来访问持久内存设备。这是一条具有挑战性的路线,因为它需要对持久内存设备和CPU功能有深入的了解。为一个服务器平台开发的应用程序可能无法移植到其他平台或 CPU 代系。

继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存