没有合适的资源?快使用搜索试试~ 我知道了~
ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月××Li B PM:简化应用程序对持久内存的使用Leonardo Marmol、Mohammad Chowdhury和Raju Rangaswami,美国佛罗里达国际大学持久性存储器设备呈现出与已经为其构建应用的现有技术唯一不同的属性。 不幸的是,构建应用程序的传统方法既不能有效地利用这些新设备,也不能为程序员提供无缝的开发体验。 我们已经构建了LIBPM,一个持久内存库,它实现了一个易于使用的容器抽象来使用PM。LibPM因此,LIBPM通过提供一个简单而高性能的事务更新机制,LIBPM允许应用程序以内存的速度操纵持久数据。LIBPM中的容器抽象和自动持久数据发现机制也简化了将遗留应用程序移植到PM的过程。从性能的角度来看,LIB PM与最先进的应用程序库的性能非常接近,甚至经常超过它们对于PM。例如,对于写密集型工作负载,LIB PM的性能提高了195,对于写密集型工作负载,L IB PM的更好地为与最先进的PIO持久内存库相比CCS的概念:·信息系统→存储类 存 储 ;·存储器→存储和密集存储;·软件及其引擎→实时系统软件;附加关键词和短语:持久内存,下一代应用程序,应用程序持久性ACM参考格式:莱昂纳多·马莫尔,穆罕默德·乔杜里,和拉朱·兰加斯瓦米。2018. LIBPM:简化应用程序对持久内存的使用。ACM Trans. Storage14,4,Article 34(December 2018),18 pages.https://doi.org/10.1145/32781411介绍对高性能数据持久性的需求是应用程序开发的基础问题。应用程序开发人员不断寻找新的和聪明的方法来在缓慢但大的持久性存储和快速但小的易失性内存之间移动数据。然而,下一代持久存储器(PM)设备提供了有趣的机会,以重组传统的IO堆栈,以获得更好的性能。首先,字节可寻址性和持久性的二分法在PM中不再存在第二,PM的读/写时间与DRAM的读/写时间相当[6,9,16]。PM的独特属性使其成为CPU总线附件的合适候选者 ,并通过加载和存储指令进行访问。因此,ReRAM、STT-MRAM、PCM和3D-XPoint等下一代PM器件促使人们重新思考传统的OS存储接口作者Marmol,M.Chowdhury和R.Rangaswami,佛罗里达国际大学,计算与信息科学学院,11200 SW第8街,迈阿密,佛罗里达州33199;电子邮件:{marmol,mchow 017,raju}@ cs.fiu.edu。允许免费制作本作品的全部或部分的数字或硬拷贝,以供个人或课堂使用,前提是制作或分发副本的目的不是为了盈利或商业利益,并且副本的第一页上有本声明必须尊重作者以外的其他人拥有的本作品组件的版权。允许使用学分进行摘要以其他方式复制、重新发布、在服务器上发布或重新分发到列表中,需要事先获得特定许可和/或付费。从permissions@acm.org请求权限。© 2018计算机协会。1553-3077/2018/12-ART34 $15.00https://doi.org/10.1145/327814134ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月三十四:2L. Marmol等人已经提出了不同类别的解决方案来解决从这些较新类型的存储器导出的阻抗失配的问题。第一类解决方案为PM使用提供系统级支持,包括构建PM感知文件系统[7,9,25,26]、PM优化块设备[2,3]、基于RDMA的PM文件系统后端[28]以及共享持久页面上基于区域的原子耐久性[4]。第二类解决方案侧重于支持程序员直接访问驻留在PM中的持久化对象。已经提出了多种编程抽象[5,10,23],并且还正在开发用户级PM库[19]在这篇文章中,我们重新评估了第二类解决方案,并提出了一个简单易用的解决方案,为PM为基础的持久化对象。持久化对象的主要目标是为程序员提供直接访问PM上进程地址空间中的对象的方法理想情况下,程序员可以访问一个持久堆,允许他们以不同的方式分配、修改和重新加载对象。适用范围虽然NVheaps [5],Mnemosyne [23]和P.IO [19]提出了鲁棒的方法,要做到这一点,在所有情况下,处理持久对象的方法都不像处理易失性对象那样简单。所有这些解决方案都需要显式地注释持久化对象,并使用特定的关键字标记持久化事务与其他解决方案不同,HEAPO [11]提供了本机堆管理,而不是在持久文件系统之上构建堆它还提出了通过采用基于键值存储的日志记录机制来进行仅撤消日志记录。但是,它仍然期望开发人员像其他解决方案一样注释事务然而,SoftPM [10]提出了更简单的抽象,但仅适用于基于块的存储。通过引入正交持久性(即,从特定已知根对象生成的对象的自动持久化)。这些解决方案NVheaps [5]、Mnemosyne [23]和P2P.IO [19]并不完全支持正交持久性的有用概念在这篇文章中,我们建立和评估LIBPM。LIBPM创建了持久容器的抽象,这是应用程序使用PM的一种新方式容器也可以被标识为持久对象存储或持久堆。在本文中,我们不讨论Linux容器(例如,Docker容器);相反,我们所指的LIB PM容器表示持久对象存储或堆。LIBPM容器通过使用熟悉的接口[10]简化了应用程序的PM消费,并实现了相关更新的一致性,同时保留了PM的性能优势应用程序创建容器并使用内存中的数据结构;用户级别的li-cache基于单个简单的应用程序提示使用指针追踪技术自动发现要持久化的容器驻留数据使用该模型,避免了大多数传统的持久化操作因此,容器还简化了现有应用程序的移植和使用PM的新应用程序的创建容器数据被映射到进程地址空间,并且通过向应用程序公开的简单事务操作来实施数据一致性容器库保留应用程序对象之间的关系(via指针)。容器的设计是为了解决几个目标,这些目标对于采用任何PM软件解决方案都至关重要。首先,它通过从内存和文件接口中借用最简单的元素来实现一个现有系统所熟悉的接口其次,它创建了一个简单而有效的事务模型,在使用PM时提供数据一致性,运行时开销低,不需要CPU ISA更改。最后,现有应用程序的移植变得简单。因此,容器填补了关键的软件空白,以确保在应用程序市场中采用PM技术LiBPM三十四:3ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月××Fig. 1. LiB PM Architecture.在本文中,我们演示了使用LIBPM进行编程比使用最先进的PIB.IO持久内存库要简单得多根据我们的经验,使用LIB PM开发类似的数据结构需要的代码行数比PIB.IO少 7%到27%更重要的是,被删除的代码具有很高的开发复杂性;我们用一个真实的开发用例来说明这一点。此外,与P. IO相比,使用LIB PM开发的应用程序的性能对于写入密集型工作负载提高了195,对于读取密集型工作负载在下文中,我们将激励LIB PM的设计和实现,讨论其在SoftPM项目中的起源,强调其演变背后的设计原则,并将其与持久性内存空间中最先进的贡献进行定性和定量2架构LIBPM在逻辑上和架构上分为两个组件,核心和后端。这些组件处理对应用可见的功能以及与OS资源内的持久存储器管理机制的交互。 LibPM的所有功能都可以通过它的API应用于应用程序。在图1中描述了LIBPM的一个命中实例,并在本节的其余部分中进一步讨论了该实例。2.1应用编程接口我们相信,一个新的持久内存API将会出现,至少有两个指导原则。最重要的是,大多数应用程序开发人员将期望用于持久内存的易失性内存API的简单性,但将期望用户级库处理与正确使用持久内存相关的许多微妙之处因此,理想情况下,API应该提供确保这种访问安全进行的功能,以便在崩溃后不会破坏持久的应用程序状态。其次,实现API的库层必须足够低的开销,以便保持使用持久存储器优于基于块的持久存储的性能优势根据这些设计原则和我们以前三十四:4L. Marmol等人ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月表1.LiB PM API类别API描述容器管理open(.)关闭(...)clear(.)删除(...)打开/创建容器。关闭打开的容器。清除容器内容物。删除所有容器数据和元数据(持久性)。内存分配pmalloc(.)pfree(...)在容器中分配持久对象释放持久对象。交易记录更新提交(.)rollback(.)原子地将所有挂起的更新应用到容器。撤消对容器的所有更新注释setroot(.)getroot(.)排除(...)将object设置为容器的根获取根对象的地址。在容器密封过程中,请勿遵循指针在软件持久内存(SoftPM)[10]的工作中,我们提出了一个API,它将类似内存的接口与容器抽象相结合。建议的空气污染指数归纳于表一,并分为四个主要类别。第一类是容器管理,处理与容器相关的操作。LIBPM库为容器提供了一个它维护足够的信息来映射容器ID与构成容器的底层持久文件。它允许程序员通过一个简单的API调用- open来创建或恢复容器一个容器只能由一个应用程序独占打开LIBPM在允许应用程序访问容器之前检查容器的状态开发人员可以关闭打开的容器或删除现有的容器,从而分别使用close和deleteAPI调用回收容器使用的PM第二类允许开发人员使用pmalloc在给定的容器中创建任意复杂的数据结构,并使用pfree将它们从容器中释放出来。应该注意的是,除了托管对象之外,容器还通过存储额外的元数据来保存对象之间的关系(指针)这些附加信息使得容器可以在保持指针正确性的同时,在任意内存位置正确地恢复持久对象第三类实现事务模型。提交操作原子地保存对给定容器所做的所有更改或者,可以通过发出回滚调用来中止所有更改最后一个类别定义了容器上与注释相关的操作这允许程序员分别使用setroot和getroot设置和获取容器容器根指定计算内存闭包时的起始点以标识需要持久化的所有数据2.2核心LIBPM Core管理应用程序创建的容器,以提供透明的持久内存分配和事务持久性支持。Lib PM核心的四个互连组件-内存分配器、事务管理器、指针管理器和发现-提供这些功能。这些组件一起实现了LIBPM API。内存分配器负责分配任意大小的持久对象pmalloc从后端组件的页面分配器提供的映射页面中分配内存它实现了slab分配的一个变体[1],其中对象根据它们的大小分组在一起,以最大限度地减少碎片。除了加速分配之外,这种基于slab的分配技术还允许有效地实现释放持久内存。LiBPM三十四:5ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月事务管理器确保相关更新的全有或全无持久性语义,即,从应用程序的观点来看相关的对持久性存储器的所有更新在高级别上,这是通过维护持久对象的两个版本来实现的:反映先前一致状态的快照版本和其中就地执行更新的当前版本一旦容器在将所有必要的修改吸收到当前版本中之后处于新的一致状态,则应用可以(i)通过发出提交操作来使当前版本持久,或者(ii)通过发出回滚操作来撤销对容器的所有更新,这将使容器恢复到其先前,即,快照,版本。可以推断,由于保持两个版本(即,快照和当前)L1BPM可以消耗比在最坏情况下绝对必要的两倍多的存储器但是,我们并不期望对于大多数工作负载,所有容器页面都同时变脏。存储器和存储器存取的工作集特性,在任何给定的时间,整个存储器/存储器空间的一小部分通常被有效地存取因此,我们预计容器中的一小部分片段在任何给定时间都不会提交指针管理器负责确保持久对象之间的关系(指针)始终得到保留为此,它存储描述持久指针的位置和目标地址的元数据,以便当对象在内存中移动时可以纠正这些指针值,以及当容器重新打开时正确恢复后者在LIBPM无法保证容器将映射到进程地址空间内的相同偏移量的环境中尤为重要最后,发现组件基于容器的根数据结构的应用程序指定的注释来实现持久数据的自动发现CIL [17])。该组件遵循SoftPM工作[10]中描述的内存闭包发现的基本方法。该组件简化了新应用程序的开发以及现有应用程序到持久内存的移植。2.3后端LIB PM的后端负责实现持久性。 后端由两个子组件组成:页面分配器和刷新器。页面分配器向上层导出一个接口,允许它们分配和释放持久内存页面。关于如何分配或释放持久性页面的具体细节由页面分配器模块处理这些模块可以自由地以他们认为合适的方式实现接口,允许他们应用特定于所使用的操作系统的不同方法和优化容器由多个段组成最初,一个64MB的片段被分配给一个容器,更多的片段会根据需要分配。但是,可以配置分配的大小以匹配系统要求。Flusher的主要任务是保证写入是持久的现代系统使用位于CPU和内存之间的多级缓存和内存控制器。正因为如此,同步更新必须小心对待,以保证他们使其持久的媒体。根据可用的指令和硬件的其他细节,Flusher自定义易失性CPU缓存和PM之间的数据传输,以优化性能。3设计在本节中,我们描述了我们在设计LIBPM时所做的许多权衡我们首先陈述了塑造我们设计的假设以及对硬件趋势的预测然后我们三十四:6L. Marmol等人ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月清单1.使用容器将RB Tree迁移到持久内存描述容器抽象和我们的事务模型,它们是我们库的核心我们将讨论如何在LIBPM中管理持久内存指针,并将其与管理持久内存指针的其他方法进行最后,我们讨论了LIBPM的局限性3.1假设我们假设一类持久性存储器的字节寻址能力和足够的性能,使直接通过内存总线的CPU访问此外,我们假设传统的CPU体系结构的内存访问保持基本不变。换句话说,持久性存储器将继续作为页面进行管理,然后通过页表在OS和CPU复合体之间进行管理,其中硬件加速访问由易失性CPU高速缓存的层次结构实现CPU指令集可能会在未来发展为支持持久内存[8,16,23]。虽然这些可能会影响我们开发的机制的细节,但它们不会从根本上改变我们在本论文中解决的核心研究问题此外,虽然独立重要,但其他研究人员正在解决诸如提高持久存储器技术的可靠性和耐久性等问题[13,20],并且不在本文的范围内。3.2PM容器抽象我们之前在软件持久内存(SoftPM)上的工作[10]开发了一个库,允许应用程序创建数据容器来持久存储任意结构化数据。SoftPM容器为开发人员提供了一个强大的抽象,开发人员只需标识一个要持久化的顶级容器根数据结构从这个顶级数据结构(通过指针)可访问的所有数据都被库基础设施自动发现并原子地持久化例如,根结构可以包含指向持久性存储器中的所有用户定义的数据结构的指针清单1描述了一个内存中的二进制搜索树的例子,它使用容器迁移到持久内存中,只需要几行代码。容器可用于持久存储应用程序内存中数据的一部分,并保证这些数据是自包含的。容器提供参考(例如,基于指针的)完整性和完整性以及原子数据持久性。SoftPM的主要设计目标是创建一个易于使用的界面,类似于今天使用易失性内存的方式,更新容器的事务一致性,以及低开销的持久性。应用程序指定的持久性点表示持久性存储器中应用程序数据的一致状态,并允许应用程序进行简单的故障恢复。LIBPM实现了SoftPM的容器抽象的混合存储器系统,这是由易失性DRAM和其他CPU可寻址的持久性存储器。然而,LIBPM仅设计用于最终将完全驻留在持久性存储中的数据结构。因此,为混合存储器数据结构提供支持的系统[18,27]与Lib PM不可比较。此外,字节可寻址PM的持久性要求与SoftPM构建的基于块的存储的持久性要求不同。有序LiBPM三十四:7ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月需要以允许开发者容易地控制这些操作但需要最小的开发者努力的方式来设计跨系统的多个核心上的易失性高速缓存上的数据的持久性这需要小心使用CPU指令来强制执行缓存行刷新和写屏障,并使用处理器制造商引入的更新的PM优化指令(例如,英特尔LIB PM虽然深受SoftPM设计原则的影响,但在其设计和实现方面存在显著差异。LIBPM的LIBPM引入了可以直接映射到进程虚拟地址空间的段的概念正如我们在3.2.1节中所讨论的,数据段保存持久化对象,并允许应用程序直接访问对象,从而获得字节可寻址性的好处SoftPM使用易失性内存作为对象的临时占位符,然后将其写入基于块的存储。相反,LIBPM通过使用页面级日志记录(第5.4.3节)最大限度地减少了对PM(持久数据的目的地)的缓存刷新。最后,如3.3节所述,LIBPM通过使用预写元数据日志和写时复制数据日志段来实现与SoftPM不同的事务。3.2.1片段。LIBPM容器是段的集合。每个段都是一组映射到进程地址空间的连续页面,以便直接访问。有三种类型的段:元数据、数据和DLog。每个容器只有一个元数据段,其中存储了容器的所有自描述信息。元数据段还托管(i)根指针,从中可以找到容器中的所有其他对象,以及(ii)数据日志,用于使容器中的所有更新原子化。数据段负责托管所有持久化对象。因此,分配大小受数据段大小的限制;这是一个可配置的参数,可以在容器创建时更改。随着更多的对象被分配,更多的数据段被创建,容器根据需要增长。每个数据段承载给定大小的对象,使内存管理简单而快速。为了减少数据段的数量,内存分配被向上舍入到下一个倍数,16个字节。这也是容器的一个可配置属性最后,还有DLog段,用于存储数据段中存储的对象的一致版本,以便进行崩溃恢复。就像数据段一样,Dlog段是按需分配的。事务所做的更改越多,就需要创建越多的Dlog段。当一个事务成功完成时,它的Dlog段可以被重用或垃圾收集。3.3交易在为Lib PM设计事务机制时,我们希望在性能和易用性之间取得良好的平衡。其结果是一个事务模型,它将内存保护机制与撤消日志记录相结合,并以页面粒度进行操作最重要的是,这种方法消除了程序员在修改数据之前明确需要修改什么数据的需要,因此与该领域的最新成果相比,大大简化了开发[5,24]。LIBPM简单地将段映射到进程地址空间,而不需要写权限,并注册一个错误处理程序来跟踪更新。因此,存储器保护允许更新由LIBPM记录,而无需开发人员参与。LIBPM不支持多个应用程序访问容器;相反,它为每个容器保留快照的本地版本然而,LIBPM可以处理同一应用程序中的另一个线程试图并行更新数据的情况我们的假设是,当数据一致时,线程将在它们之间进行通信,以在特定的执行时间点访问/提交数据结构三十四:8L. Marmol等人ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月图2(a)示出了使用大小为三个页面的段从最近打开的容器开始的对容器的更新的LIBPM处理当一个容器被打开时,所有的数据段都是写保护的。首先,应用程序尝试更新在第三段中分配的对象(图2(b))。由于承载对象的页面被写入,因此LIBPM的故障处理程序然后,整个页面被复制到一个非完整的Dlog段(图2(c)),并添加一个日志条目(图2(d))。最后,页面变为可写(图2(e)),更新继续进行。对同一页面的后续更新不会触发额外的故障,并且可以在没有任何开销的情况下继续进行(图2(g)和(h))。3.4持久内存指针除了保证持久对象的一致性和持久性之外,LIBPM的容器还需要保持它们所托管的对象之间的关系(指针)。为了更好地理解这意味着什么,让我们考虑一个容器,它托管一个m个节点N1,N2,.的链表。 ,Nm,使得Ni指向Ni+1。 当这个容器被打开时,不能保证节点将在相同的时间被映射。 现代系统默认执行地址空间布局随机化(ASLR),因此列表的下一个指针可能会被破坏。为了解决这个问题,LIBPM在容器中存储了足够的关于它托管的对象的元数据,以便当容器打开时,所有指针都在控制交给应用程序之前被固定固定一个持久指针仅仅意味着更新指针的目标地址在最坏的情况下,这些更新可以在O(n)时间内执行,其中n是容器中持久指针的数量然而,在实践中,我们已经看到,这可以通过提示mmap系统调用在运行时容器段应该映射到哪里来由于这是由LIBPM在运行时执行的动态操作,因此ASLR约束不以与它们将应用于静态映射持久对象的方式相同的方式应用于mmap提示以前的工作已经提出了其他解决方案的持久指针管理问题。其中大部分可分为三类:固定地图。这些解决方案只是通过禁用ASLR来强制所有持久对象映射到相同的地址空间位置。然而,不建议禁用ASLR,因为它使操作系统更容易受到各种类型的安全攻击[21]。指针旋转。这类解决方案使程序员负责为需要持久化的每种数据类型提供序列化方法[14]。缺点是,这些序列化方法必须在数据类型更新时更新胖指针对于这类解决方案,指针不包含目标地址。相反,它们包含足够的信息来在运行时计算目标地址[19]。这个方法有运行时指针解引用的开销,但是它允许持久对象映射到地址空间中的任何地方。3.5限制LIBPM的简单设计有两个局限性。3.5.1单次A组传输。 LIB PM的事务模型描述了每个容器的单个活动事务。虽然这对于许多应用程序来说可能已经足够了,但某些并发应用程序可能需要更多地控制何时使特定执行线程所做的更新成为持久的。LiBPM三十四:9ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月图二、LIBPM页面日志记录:(a)成功打开后容器的状态(b)应用程序试图更新对象D,其被映射为只读。(c)更新触发错误处理程序,错误处理程序通过复制包含错误地址的页面来启动日志记录机制。(d)记录更新。(e)页面是可写的。(f)由于网页是可写的,更新按计划进行,无需用户干预。(g)随后对同一页的更新不产生任何额外费用。(h)最后状态。三十四:L. Marmol等人ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月图3.第三章。容器密封:(a)初始状态。(b)将所有可到达的节点移动到容器。(c)移动所有(d)修复反向引用。3.5.2共享集装箱是一个失败的过程。 LibPM的持久内存模型的一个直接后果是容器不能在多个应用程序之间共享。由于相同的容器可以被映射到不同的地址以用于共享进程,因此目前没有简单的方法来以对于两个应用都正确的方式跟踪和固定指针位置和指针值在正在进行的工作中,我们依赖于区域系统[4]提供的新机制来支持共享LIB PM容器。4将旧应用程序移植到PMLIBPM提供了一种机制,可以将遗留应用程序增量地移植到PM,而不必更改核心应用程序逻辑。为了说明该过程,我们参考图3(a),其描绘了二叉搜索树(BST)从易失性存储器到由LIBPM管理的PM的迁移首先,开发人员创建一个新的容器,并在其中分配一个指向树头部的根对象。然后,在下一次提交操作中,LIBPM将向容器中添加所有可从容器中托管的根R×访问的对象由于节点4是第一个到达的节点,因此它首先被移动到容器中,如图3(b)所示在一个简单的广度优先遍历之后,从树的头部可到达的所有节点最终都会被移动到容器中,使每个节点都是持久的。重要的是要注意,也可以采用其他图遍历算法来遍历树以实现相同的结果。随着节点的移动,它们的易失性分配被释放,因此总内存消耗在一个常数因子内保持大致相同。图3(c)描述了整个树被持久化的状态此时,整个树都在持久内存中,可以使用。然而,旧的volatile根和迭代器(在图3(c)中描述为Itr)不再有效,因为它们指向释放的内存。容器发现过程的最后一步是修复这些悬空指针,以便更新它们的目标地址图3(d)描述了提交之后的内存状态LiBPM三十四:ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月操作完成。迁移其他数据结构可以用类似的方式完成,只需要大约相同数量的代码行有关详细信息,请参见清单15评价对LIBPM的评估有三个目标:(1)验证我们实现的正确性(2)与以前的解决方案相比,测量LIB PM的易用性,以及(3)评估LIB PM的性能并将结果与以前的解决方案进行比较5.1方法由于目前市场上还没有持久性内存硬件,因此我们依赖于硬件和软件仿真。我们所有的实验都是在英特尔PMEP环境还允许对PM访问时间进行细粒度的延迟控制,并支持专门为PM开发的Intel这台机器配备了Intel(R)Xeon CPU E5-4620 v2@2.60 GHz,32 GB的易失性内存,8 GB的持久性内存,以及定制版本的Linux内核v4.1.18。我们将我们的系统与Intel开发的最先进的持久化对象库P.IO(www.pmem.io)进行了比较。这是我们所知道的最新和最广泛使用的PM库。我们与PIB.IO库的比较突出了Lib PM的核心特性,包括显着降低开发复杂性以及性能提升。5.2正确性为了验证LIBPM的正确性,我们使用了两种众所周知的技术:(1)测试覆盖率分析和(2)故障注入。随着LIB PM的实现,我们还开发了一个测试套件,可以100%地执行LIB PM代码,正如Clang/LLVM代码覆盖工具所报告的这个测试套件包括许多单元测试和几个系统测试。单元测试验证每个单独组件的正确性,而系统测试验证密切相关组件之间的相互依赖性除了代码覆盖率,我们还在事务和恢复逻辑的关键部分中包含的几个故障点执行了故障注入不同故障点的故障可以按需触发,也可以在随机时间点触发,这允许我们在检查中崩溃应用程序,并验证容器的状态始终等于在最近一致性点找到的版本在每种情况下,LIB PM都保持了容器的一致性,让我们相信我们的实现是合理的。5.3易用性直接编程PM具有挑战性。正因为如此,以前的解决方案都提出了更高级别的抽象来隐藏与PM相关的所有怪癖和复杂然而,所有以前的解决方案都实现了一个事务模型,使用户负责手动记录更新[5,19,24]。这种模型提供了对持久性和性能的充分控制,但也很难正确使用,这使得它容易出错,并且难以调试。出于这个原因,我们避开了传统的日志记录方法,而是探索了一种不需要用户干预的自动日志记录技术我们通过计算与以前的方法相比,实现流行的数据结构所需的代码行(css)来量化我们的方法的易用性从表2中可以看出,使用PIB.IO实现持久化数据结构比使用LIBPM需要更多的时间对于我们所考虑的数据结构,开销各不相同三十四:L. Marmol等人ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月表2. 统计持久化流行数据结构的需求数据结构LIB PMP.IO跳跃列表192264(+27%)哈希表285337(+15%)RB树387417人(+7%)B树428五百一十一宗(上升百分之十六)较小的值是可取的。见图4。树的旋转清单2.使用LiB PM实现RBTree旋转方法。从7%上升到27%。为了充分理解为什么会出现这种情况,让我们仔细看看代码。作为一个例子,我们使用LIB PM和P. IO检查RB Tree实现中的rbtree_rotate函数这个函数可以作为定性比较的一个很好的输入,因为创建一个函数原型需要大约十几行不平凡的代码。此外,它还说明了P2P.IO实现不仅需要额外的代码行,而且这些额外的代码具有相当大的复杂性,使开发容易出错。rbtree_rotate操作要求的形式描述可以如下:给定树的一个节点和一种旋转类型(见图4),执行所述旋转以在O(1)中调整RB树的高度。清单2给出了在使用Lib PM时旋转持久RB树的代码。敏锐的读者会注意到,如果使用此代码来旋转易失性RB树,则不需要修改它这是因为LIBPM无缝地处理了透明的日志更新和指针解引用完整性,而无需开发人员参与。换句话说,将现有LiBPM三十四:ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月清单3.使用PmE m.IO实现RBTree旋转方法考虑到对易失性数据结构的操纵是任何应用程序所需的步骤,使用LIBPM的应用程序以及使用LibPM编写新应用程序是非常清单3显示了现在使用P2.IO实现的相同函数首先要注意的是,函数的签名(第2行和第3行)已经更改。开发人员需要了解持久数据类型,并在程序创建期间不断使用这些知识需要使用宏TOID来创建持久指针。与常规指针不同,持久指针不存储进程地址空间中的绝对偏移量值相反,它们存储元数据和相对偏移量,用于在运行时计算指针的目标在函数的其余部分,其他持久指针以类似的方式声明在第7行和第8行中,我们看到使用了另一个宏TX_ADD,它将node和child的值记录为正在进行的事务的一部分,以便在事务提交失败时处理恢复这些步骤是必要的,因为这两个变量最终都需要修改。因此,需要小心地使用P.IO,在持久化数据被修改时保持跟踪,并确保在发生崩溃时安全地存储原始值在第9行中,我们遇到了一个新的宏D_RW,用于解引用持久指针。此时,进行赋值是安全的,因为原始值已被安全地记录。在第10行,我们看到在两个持久指针的比较操作中使用了另一个解引用。如果满足条件,则父指针在使用TX_SET宏记录其当前值后进行更新对于函数的其余部分,使用相同的宏进行更多的赋值和指针取消引用。清单3的复杂性并不是P.IO所独有的它仅仅是P.IO使用的持久指针和事务模型的直接结果,并被其他先前的工作[5,24]所共享虽然这些模型确实为高级用户提供了更大的控制,但它也使非专家很难使用PM。出于这些原因,我们认为LIBPM在性能和编程复杂性之间取得了有价值的平衡。5.4性能我们评估了LIBPM的性能,并将其与Intel的最先进的持久内存库P.IO进行了IO提供了一组具有跨平台支持的持久化数据结构,我们也将其移植到了LIB我们考虑的数据结构如下:三十四:L. Marmol等人ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月×××××××图五、(a)PME M.IO vs LiB PM(P)。(b)PmE m.IO vs LiB PM(查找)。(c)PME M.IO vs LiB PM(P)。Hashmap一个哈希表,通过将同一个桶中的条目链接在一起来解决冲突BTree一个常规BTree的内存版本,分支因子为8。一个标准的红黑树(RBTree)。跳过列表每个节点有一个值和八个级别的跳过列表这些数据结构被修改,以便向用户公开的每个操作都是原子的和持久的。我们特别注意,每个实现都与两个库具有相同的一致性和持久性:LIB PM和P. IO。这些数据结构中的每一个都可以在64位整数和任意大小的值之间创建映射对于这些实验,我们简单地存储了一个NULL值,以更好地测量利用数据结构的核心逻辑的开销图5(a)描述了对Lib PM和PIB.IO的所有持久数据结构的插入操作进行比较的结果。我们看到,对于每个数据结构,LIBPM的性能都优于P.IO,从Skiplist的51到BTree的195。另一方面,对于查找,差异范围从Skiplist的1.3到BTree的2.6。最后,对于Skiplist和Hashmap,删除操作的每秒钟差异分别从47到206不等,两者都有利于LIB PM。乍一看,这些结果似乎与直觉相反。当L ib PM必须为平均事务记录更多的数据时,L IB PM的性能怎么可能超过P.IO?这个问题的答案有三个部分:持久指针模型、影子数据结构和日志粒度。5.4.1持久指针模型。 IO和LIB PM对于持久指针有非常不同的模型。在P2.IO的情况下,持久指针被定义为包含元数据和偏移量的类型请参见清单4中的详细信息这意味着对于PIO,持久指针不仅仅是进程地址空间中的偏移量,而是一个具有足够信息的对象,可以在运行时计算目标地址有了这个间接层,IO就可以在地址空间中的任何地方映射持久对象,更重要的是,可以轻松地在多个进程之间共享持久对象因此,任何涉及取消引用多个指针的工作负载都将经历运行时降级。另一方面,LIBPM对持久化指针有一个简单得多的方法对于LIBPM,持久指针与常规指针没有什么不同,除了持久指针必须在使用之前被固定修复指针的开销仅在容器打开时支付在此之后,持久指针的使用与常规指针没有什么不同为了测量持久指针解引用的开销,我们创建了一个比CPU缓存大几个数量级的大型循环列表,并以一种使连接节点在内存中相距很远的方式链接节点然后,我们在测量时多次遍历列表,从一个节点移动到下一个节点所需的时间对于P. IO,平均而言,lap比LIB PM高2,因为P.IO需要指针解引用来获得对象的内存地址LiBPM三十四:ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月图六、分配延迟。见图7。原子更新延迟。清单4.IO5.4.2影子数据结构。持久内存上的内存分配必须以原子方式执行,以便在崩溃的情况下不会泄漏内存。重要的是要注意,当持久内存泄漏时,它会持续泄漏。与易失性内存不同,重新启动应用程序不足以回收持久性内存泄漏。出于这个原因,P.IO和LIB PM都实现了原子内存分配。IO使其内存分配原子化的方式与使内存更新原子化的方式相同,即在进行更改之前记录状态更改。但是这些日志条目必须是持久的,以保证内存的状态可以在需要时回滚。为了使日志条目持久,IO必须刷新包含条目的缓存行刷新和内存围栏操作都发生在应用程序执行路径中,增加了事务的延迟。LIBPM使用阴影位图和slab分配器实现原子内存分配-LIBPM将类似大小的持久对象打包到一个slab中,并使用持久位图跟踪slab的可用性此外,在易失性存储器中创建的位图的阴影副本当调用pmalloc时,LIBPM在阴影位图中找到可用的slab,并返回该slab的正确地址。在这之后,LIB PM可以自由地使用给定的内存地址。阴影位图跟踪包含与持久位图相关的脏位的高速缓存行,并在提交时刷新这些高速缓存行。如果在提交完成之前崩溃,则不需要做任何事情,因为持久化位图尚未修改。这种方法有效地延迟了缓存行刷新和内存屏蔽,并因此将其批处理到提交时,从而减少了运行时将开销降至最低(如图6所示)。5.4.3以页面粒度记录日志 与PIB.IO相比,LIB PM记录和刷新的数据要多得多,但平均创建的日志条目要少得多。这是以页面粒度进行日志记录的直接结果。然而,记录和刷新缓存行并不像人们想象的那么昂贵。新的英特尔CLWB指令具有与CLFLUSH和CLFLUSHOPT类似的语义,但显著的区别在于它不会使刷新的高速缓存行这具有对同一高速缓存行的后续访问不会引起高速缓存未命中的优点IO和LIBPM都使用了这种优化的指令,但区别在于内存围栏三十四:L. Marmol等人ACM Transactions on Storage,Vol.号144、第三十四条。出版日期:2018年12月他们发布。为了使日志条目持久,除了刷新高速缓存行之外,还必须发出内存围栏以强制刷新发生并保持操作的顺序。使用的内存围栏越多,对应用程序性能的负面影响就越大如Kim et al.[15],可以通过仅在属于同一事务的一组更新之后发布围栏来最小化围栏的数量由于PIP.IO为事务记录了更多更小的条目,并且积极地发出内存围栏以刷新每个日志条目,因此与LIBPM相比,它发出了更多数量的内存围栏这就是与LIB PM相比,P.IO的事务延迟较高的原因LIBPM结合了几种技术,这些技术可以最小化更新和内存分配以及删除的内存围栏数量为了更好地理解这种现象,我们设计了一个实验,在改变每个事务的更新数量和大小的同时测量事务的延迟图7总结了结果。作为基线,我们使用事务记录一个页面(4KB
下载后可阅读完整内容,剩余1页未读,立即下载
cpongm
- 粉丝: 5
- 资源: 2万+
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- C++标准程序库:权威指南
- Java解惑:奇数判断误区与改进方法
- C++编程必读:20种设计模式详解与实战
- LM3S8962微控制器数据手册
- 51单片机C语言实战教程:从入门到精通
- Spring3.0权威指南:JavaEE6实战
- Win32多线程程序设计详解
- Lucene2.9.1开发全攻略:从环境配置到索引创建
- 内存虚拟硬盘技术:提升电脑速度的秘密武器
- Java操作数据库:保存与显示图片到数据库及页面
- ISO14001:2004环境管理体系要求详解
- ShopExV4.8二次开发详解
- 企业形象与产品推广一站式网站建设技术方案揭秘
- Shopex二次开发:触发器与控制器重定向技术详解
- FPGA开发实战指南:创新设计与进阶技巧
- ShopExV4.8二次开发入门:解决升级问题与功能扩展
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功