技术与生活 性能之重 持久内存编程指南—乱译连载 (15.性能分析与性能)

持久内存编程指南—乱译连载 (15.性能分析与性能)

持久内存系统的分析和性能优化技术与无持久内存系统的那些技术类似。本章概述了理解性能的一些重要概念。也提供了指导用于刻画已存在无持久内存应用的特征和理解是否适用于持久内存。最后,提出了运行在持久内存平台上应用性能分析和调优的重要度量指标,包含一些例子,如何使用VTune Profiler来收集数据。

15  性能分析与性能

15.1  简介

本章首先讨论分析内存和持久内存的通用概念,以及如何识别出使用高性能持久存储和高容量易失内存的机会。然后描述了可以帮助你优化代码来达到最好性能的工具与技术。

性能分析需要工具来收集具体的数据以及应用、系统和硬件性能度量数据。在本章,我们讲述如何使用Intel VTune Profiler来收集这些数据。许多其他数据收集选项是可用的;我们所讲述的技术是很有价值的,不管数据是如何收集的。

15.2  性能分析概念

持久内存性能分析的大多数概念与那些已经建立的共享内存程序或者存储瓶颈的性能分析概念类似。本节粗略描述了分析和优化PM性能的几个重要性能考虑因素,定义了本章我们使用的术语和环境。

15.2.1 计算限制 vs. 内存限制

性能优化主要是确认出当前性能瓶颈并改善它。计算限制负载的性能通常被CPU每个周期处理的指令数量限制。例如,在无许多依赖的非常小量的数据上做大量计算的应用,通常是计算限制。这类型的负载CPU越快就运行得越快。计算限制应用通常有很高的利用率,接近100%。

相反,内存限制负载的性能通常被子系统的内存和从cache和系统内存获取数据的长延迟而限制。一个例子是应用随机访问来自DRAM中数据结构的数据。此种情况下,增加更多计算资源不能改善这个应用的性能。对于内存限制负载,增加持久内存来改善性能通常是一个好的选择,而对于计算限制负载则不是一个好选择。内存限制负载通常CPU利用率比计算负载低,由于内存传输表现出CPU暂时停顿,以及有高内存带宽。

15.2.2 内存延迟 vs. 内存容量

在讨论持久内存时,这个概念必不可少。此讨论中,我们假设DRAM访问延迟低于持久内存并且系统持久内存容量大于DRAM。被内存容量限制的负载可以通过增加易失性模式的持久内存来获得收益,被内存延迟限制的负载则不太可能从中受益。

15.2.3 读 vs. 写性能

每个持久内存技术都是独特的,理解读写性能的不同之处至关重要。不同媒体类型表现出不同程度的不对称同步读写性能特性,通常读比写快很多。因此,理解负载的读写混合比和应用负载中的存储影响对于理解和优化性能很重要。

15.2.4 内存访问模式

内存访问模式是系统或者应用从内存中读写的模式。内存硬件通常基于时间局部性(访问最近使用的数据)和空间局部性(访问连续的内存地址)来进行优化,达到最好性能。这经常通过快速内部缓存的精心组织和智能预取器来达成。访问模式和局部性水平会剧烈地影响缓存的性能,也影响并行度和共享内存系统里的负载分布。缓存一致性也影响多处理器性能,意味着某些内存访问模式会在并行时有天花板。已存在许多清晰地内存访问模式,包括但不限于顺序(sequential)、交叉(strided)、线性(linear),以及随机(random)。

只运行一个应用的系统上,容易度量、控制和优化内存访问。在云上和虚拟化环境中,访客可以运行任意类型的应用和负载,包括:web server、数据库,或者应用Server。这使得确保内存访问是为硬件完全优化更为困难,因为访问模式本质上是随机的。

15.2.5 IO限制负载

一个程序如果IO子系统超快就运行超快,它就是IO限制负载。我们主要对基于块的磁盘IO子系统感兴趣,但这也包括其他子系统,如:网络。IO限制状态不是预期的,因为它意味着CPU在等待数据加载或者卸载到主存或者存储时必须暂停。依赖于数据的位置和存储设备的延迟,这会激发当前应用线程和其他应用线程间的自发上下文切换。当一个线程阻塞时,就会主动发生上下文切换,因为它需要的资源不能立即可用,或者需要很长时间才能做出响应。随着每一代计算机的速度的不断提高,避免IO限制状态就更为强烈。消除IO限制经常比更新CPU和内存来改善性能性价比更高。

15.3  确认负载是否适合持久内存

持久内存技术不可能解决每个负载性能问题。当考虑持久内存时,你应当理解负载和它运行的平台。举个简单例子,考虑计算密集型负载,它很大程度上依靠浮点运算。该应用的性能可能受限于CPU上的浮点计算单元,而不是内存子系统的任何部分。此种情况下,增加持久内存对应用性能没有什么影响。现在考虑一个需要大量从磁盘读写的应用,磁盘访问很可能是瓶颈,增加一个更快的存储将解决问题,如:持久内存,能够提升性能。

这些只是沧海一粟,沿着这个谱系应用有各种不同的行为。理解寻找这些行为并度量他们是使用持久内存的一个重要步骤。本节展示了标识和确认应用是否适合持久内存的重要特征。我们考虑那些需要内存持久化、以易失性内存方式使用持久内存,或者两者皆有的应用。

15.3.1 易失性使用场景

第10章描述的几个库和使用场景,应用利用了持久内存的性能和容量来存储非易失性数据。对于易失性场景,持久内存扮演了平台的一个附加内存层。它对应用透明,例如使用Intel Optane DC 持久内存支持的内存模式,或者应用修改代码使用如libmemkind库来执行易失性内存的分配。这两种情况下,内存容量限制的负载将从增加持久内存中受益。应用性能将得到很大的改善,如果工作数据集可以放进内存并避免以页进出磁盘。

15.3.2 识别负载是否内存容量限制

确认负载是否内存容量限制,你必须确认应用的内存占用量。内存占用量是应用在其生命周期同时发生的分配内存的高水位线。因为物理内存是有限的,你应当考虑的一个事实是操作系统和其他进程也耗费内存。如果操作系统和所有内存消费者接近或者超过可用DRAM容量,那么增加内存应用会受益,因为应用可以在DRAM中放下其数据。许多工具和技术可以用来确认内存占用量。VTune Profiler有两个不同的方法来获得信息:内存消费分析或者平台分析器分析。VTune Profiler的Linux、Windows版本免费下载:https://software.intel.com/en-us/vtune.

内存消费分析追踪应用分配的所有内存。图Figure 15-1 展示了 VTune Profiler 自底上上的报告,表示了所分析应用整个时间段的内存消费情况。Y轴最高值表示应用占用量接近1GiB。

图Figure 15- 2是内存使用率(占所有可用内存的百分比)图,展示了使用操作系统统计工具度量并产生的时间线图。

图Figure 15-2 的结果取自与图Figure 15-1不同的应用。该图展示了非常高的内存消耗,暗示该工作负载可以通过增加内存来提升性能。如果你的持久内存有多种模式,如:在Intel Optane DC上有内存模式、应用直访模式等。你首先需要更多的信息来确认所要使用的模式。下一个重要信息是热工作集的大小。

15.3.3 识别负载的热工作集大小

持久内存与DRAM特性不同。因此,你应当明智的选择数据存放在哪里。我们假设访问持久内存上的数据比DRAM延迟更大。在给出的选项:访问DRAM数据还是持久内存数据时,我们应当选择DRAM,因为其有更好的性能。然而,当DRAM不能装下所有数据时,增加持久内存就有用了。你需要理解你的负载如何访问数据,才能决定持久内存的配置。

工作集尺寸working set size (WSS)是应用工作时需要的内存量。例如,如果应用分配了50GiB主存并且以页方式映射,但它执行自己的任务时每秒只访问20MiB数据。,我们就说工作集尺寸是50GiB,而热数据是20MiB。知道容量对于规划和扩展性分析很重要。热数据是应用在任一给定时间分配的那些对象的总大小。

确定工作集和热工作集的尺寸不像确定内存占用量那样直接。大多数应用的大量对象按热度分布很广,没有一个清晰的线来划清哪些对象是热的,哪些不是。你必须领会这些信息并确定热工作集大小。

VTune Profiler有个内存访问分析特性,能够帮助确定应用热工作集大小(在收集数据前选择“Analyze dynamic memory objects”选项)。一旦有足够的数据收集到,VTune Profiler将处理数据并生成报告。在GUI由底向上的视图中,格子列出了应用分配的每个内存对象。

图Figure 15-3展示了应用内存访问分析的结果。它在括号内展示了内存大小,加载和存储的数量。该报告不包含并发分配的说明。

报告识别了经常访问(加载和存储)的对象。这些对象的总大小就是工作集大小—其值被写在括号中。你决定划一条线,来确定对象是否是热工作集的一部分。

依据工作负载,没有一个容易的方法来确定热工作集大小,而应用开发者会更清楚。对于决定是否以内存模式或者应用直访模式开始,粗略的评估很重要。

15.3.4  需要持久化场景

利用持久内存持久化的场景,与使用前面所述易失性场景相反,通常使用持久内存来替换快速存储设备。这种场景确认负载的适用性是很直接、容易的。如果应用性能受限于磁盘访问(磁盘、SSD等等),那么使用类似持久内存这样的快速存储会有所帮助。有几种方法来识别应用的存储瓶颈。开源工具,如:dstat、iostat给出了磁盘活动的高层概述,像VTune Profiler则提供了更多细节分析。

图 Figure 15-4 展示了使用平台分析器收集到的NVME的吞吐量和IOPS。该例子使用了一个非易失性磁盘来扩展存储,其吞吐量和IOPS如图所示。这个应用会从使用更快的存储(如持久内存)中获益。另外一个识别存储瓶颈的更重要度量指标是IO等待时间。平台分析器分析能够提供此度量指标并显示随时间流逝如何影响CPU的利用率,如图Figure 15-5所示。

15.4  持久内存工作负载性能分析

在持久内存系统上优化工作负载遵循那些只有DRAM系统上的类似优化原则。【在持久内存系统上优化工作负载所遵循的原则与只有DRAM系统上的原则类似。】附加的一些因素请谨记如下:

  1. 写到持久内存比读对性能影响更大;
  2. 应用可以在DRAM或者持久内存上分配对象。如果随意分配,会有负面性能影响;
  3. 内存模式(特指Intel Optane DC persistent memory), 用户选择与DRAM不同的缓存尺寸来提高工作负载性能。【理解:持久内存因为有延迟,设置更大的尺寸有助于提高性能】

记住这些附加因素,负载性能优化方法将遵循相同的流程:刻画负载的特征、选择正确的内存配置、优化代码到最优性能。

15.4.1 刻画负载特征

持久内存上负载性能依赖于负载特征的组合和其下的硬件。理解负载特征的关键指标有:

  1. 持久内存带宽;
  2. 持久内存read/write 比率;
  3. 传统存储页式存取;
  4. 负载工作集尺寸和内存占用量;
  5. NUMA特性;
  6. 内存模式(特指Intel Optane DC persistent memory)的近内存cache行为。

15.4.2 内存带宽和延迟

持久内存与DRAM类似,有带宽限制。当带宽满时,应用很快到出现性能瓶颈。带宽限制依据平台而不同。你可以使用硬件详细参数或者内存基准测试应用来计算你的平台的峰值带宽。

Intel Memory Latency Checker (Intel MLC)是一个免费工具,支持LINUX和WINDOWS,可从https://software.intel.com/en-us/articles/intelr-memory-latency-checker下载【已下载,当前版本mlc_v3.8.tgz】。MLC可用于测量DRAM和持久内存的带宽和延迟,它使用了如下各种测试:

  1. 测量每个CPU socket之间的空闲内存延迟;
  2. 测量各种读写比例请求的峰值内存带宽;
  3. 测量不同带宽点的延迟;
  4. 测量从指定core请求指定内存控制器地址的延迟;
  5. 测量cache延迟;
  6. 测量来自cores/sockets子集的b/w;
  7. 测量不同读写比率下的 b/w ;
  8. 测量随机和顺序访问模式下的延迟;
  9. 测量不同跨度尺寸的延迟;
  10. 测量cache到cache的传输延迟。

VTune Profiler有内建核心支持测量系统峰值带宽。一旦你知道了平台的峰值带宽,你可以测量你负载的持久内存带宽。这会揭示出持久内存带宽是否是瓶颈。图Figure 15-6 展示了一个应用的持久内存读写带宽。

15.4.3 持久内存读写比例

如“性能分析概念“章节所描述,持久内存读写比例对于负载的综合性能扮演了重要角色。如果写相对于读比例很高,则持久内存的低延迟对于改善性能非常有帮助。使用VTune Profiler中的平台分析特性是收集这些信息的一个方法。图Figure 15-7 展示了持久内存的读流量 VS 所有流量比率。该值越接近1.0性能越好。

15.4.4 工作集尺寸和内存占用量

如“确认负载是否适合持久内存“章节所述,应用的工作集尺寸和内存占用量是理解运行在持久内存系统上的负载的重要特性。度量指标可以使用前面描述的工具和过程来收集。

15.4.5 NUMA行为

多槽平台通常为每个槽都加上持久内存。当一个槽上的线程访问另一个槽上的持久内存时,会有更长的延迟。这些远程访问是NUMA行为,影响性能。多项指标收集用来确认负载中有多少NUMA活动正在发生。在Intel平台,在多槽之间通过QPI(QuickPath Interconnect)或者UPI(Ultra Path Interconnect)来传输数据。高交互带宽意味着NUMA相关的高性能。除了交互带宽外,一些硬件还提供计数器来追踪本地和远程访问持久内存。

理解你负载的NUMA行为,是理解性能优化的另一个重要步骤。图Figure 15-8展示了VTune Profiler 采集的UPI带宽。

15.4.6 硬件调优

系统内存配置是确定系统性能的重要因素。负载性能依赖于负载特征与内存配置的组合。没有哪一种配置可以为所有负载都能提供最好的性能。这些因素使得面向负载特征来调优硬件变得十分重要。

15.4.7 可寻址内存容量

绑定的DRAM和持久内存容量确定了系统的总的可寻址内存容量。你可以调整持久内存的大小来适应负载的内存占用量。

可用的DRAM容量应当足够容纳负载的热工作集。当DRAM被充分利用时,大量的易失性通信流流向持久内存,这是一个很好的指标,表明工作负载可以从额外的DRAM大小中获益。

15.4.8 带宽需求

最大可用持久内存带宽依赖于使用持久内存模块填充的channel通道的数量。填满持久内存的系统更好的服务于高带宽需求的负载。填了部分持久内存的系统用于不是内存延迟敏感的负载。参见server配置指导原则。

15.4.9 BIOS选项

随着server平台对持久内存的采用,许多特性和选项被加到BIOS,提供附加的调优能力。这些选项和特性对于每个server厂商和持久内存产品有所不同。所有或用选项请参见server BIOS文档;大多数共用常用选项,包含:

  1. 修改电力水平来平衡电力耗费与性能。给持久内存更多的电力可以提升性能;
  2. 开启或者禁用持久内存专有特性;
  3. 调整持久内存的延迟或者带宽特性。

15.4.10 为持久内存优化软件

有许多方法来优化应用以高效利用持久内存。每个应用以不同的方式获得收益,并需要相应的修改代码。本节描述一些优化方法。

15.4.11 数据布局

数据布局是在持久内存系统上易失性负载的最通用的优化手段。应用开发者可以选择分配数据结构或者对象到DRAM或者持久内存上。正确的选择很重要,因为错误的分配将严重影响应用性能。分配通常通过特定API来进行,例如,PMDK中的可用分配API和memkind库。

如果你对代码和它如何与生产负载一起工作很熟悉,那么知道哪些数据结构和对象应该存储在内存、存储的哪一层并不难。那些数据结构和对象将是易失性的,还是持久的?这帮助寻找潜在的候选者,像VTune Profiler这样的工具能够识别last-level cache (LLC)缓存缺失的对象。目的是识别应用最频繁使用的数据结构和对象并确保他们被放到最快的媒体中,并且以适合的访问模式。例如,只写一次但读取多次的对象最好放到DRAM中。频繁更新的对象并且需要持久化的,应当放到持久内存而不是传统的存储设备。

你必须记着内存容量限制。像VTune Profiler工具能够帮助确定大概有多少热数据对象适合放到DRAM。对于很少LLC缺失的对象或者太大而不能从DRAM分配的对象,你要把他们放到持久内存中。这些步骤确保最经常访问的对象有最快的路径到CPU(分配在DRAM中),偶尔访问的对象可以有效利用附加的持久内存(而不是放到更低速的存储设备上)。

优化的另一个考虑是对象访问的load/store的比例。如果你的持久内存硬件特性load/read操作比stores/writes快很多,这必须被考虑。高load/store比例的对象更受益于驻留在持久内存中。这里没有硬性的规则来确定什么是频繁和不频繁。尽管行为依赖于应用,这些指导还是为选择如何在持久内存里分配对象给出了一个起点。完成该过程后,开始分析并调整应用以进一步改善性能。

15.4.12 内存访问优化

只有DRAM的平台上的优化cache的通用技术也适用于持久内存平台。像缓存未命中惩罚和空间/时间数据局部性这些概念对于性能很重要。许多工具可以收集缓存和内存的性能数据。VTune Profiler预置了内存每个层次的度量指标,包括如下图所示的Intel Optane DC persistent memory。

这些性能度量帮助确定内存是否是你应用的瓶颈,如果是,内存哪个层次影响最大。许多工具可以定位源码位置和造成瓶颈的内存对象。如果持久内存是瓶颈,复查“数据布局”一节来确认持久内存是否被有效使用。性能优化技术像缓存块化(cache blocking)、软件预取、改善的内存访问模式可以帮助缓解内存架构瓶颈。你必须确定如何重构软件来更有效地使用内存,并且这些度量指标可以为你指出正确的方向。

15.4.13 NUMA优化

NUMA性能问题已经在“刻画负载特征”一节描述过。我们会在第19章更详细地讨论。如果你识别性能问题与NUMA内存访问有关,那么需要考虑两件事:数据分配 vs 第一次访问和线程迁移。

15.4.14 数据分配与首次访问

数据分配是为对象分配或者预订一定数量虚拟地址空间的过程。进程虚拟地址空间是进程可以使用虚拟地址空间的集合。每个进程的地址空间是私有的,不能被其他进程所访问,除非该地址空间的共享的。虚拟地址不代表一个对象在内存中的实际物理位置。实际是,系统维护一个多层页表,一个内部数据结构用来翻译虚拟地址映射到实际对应的物理地址。应用线程每次引用地址,系统都把虚拟地址转换成物理地址。物理地址指向连接到CPU的物理内存。第19章准确描述了该操作如何工作,并展示了为什么高容量内存系统可以通过使用操作系统提供的大页或者巨页来改善性能。

软件普遍做法是在应用启动时分配完大多数数据。操作系统会偿试分配与执行线程CPU相关联的内存。操作系统调度器然后尽力总是调度该线程到它最近运行的CPU上,希望数据一直驻留在CPU cache中。在多槽系统中,这会导致所有对象被分配到一个槽的内存上,这会产生NUMA性能问题。访问远程CPU的数据招致延迟性能处罚。

一些应用延迟预订内存,直到数据被首次访问。这可以减轻NUMA问题。理解你的负载如何分配数据对于理解NUMA性能很重要。

15.4.15 线程迁移

线程迁移,软件线程被操作系统调度器跨槽移动,是NUMA问题的最常见原因。一旦对象被内存分配,从另一个物理CPU访问它们,会招致延迟惩罚。甚至分配数据在线程当前运行的槽上,除非你绑定CPU或者有其他保护措施,否则该线程未来可能移到其他任一core或者槽上。你可以通过识别线程正在运行的core属于哪个槽来跟踪线程迁移。图 Figure 15-10 展示了VTune Profiler 对此分析的例子。

使用此信息来确认NUMA线程迁移是否访问了远程的DRAM或者持久内存。

15.4.16 大页和巨页

大多数操作系统的默认页尺寸是4K。操作系统为不同应用负载及需求提供许多不同页尺寸。在Linux上,大页是2M,巨页是1G。大页尺寸在某些场景可以提高持久内存上的负载性能。

对于有大页内存需求的应用,操作系统为虚拟到物理地址转换而维护的页表尺寸也会明显变大。快表(旁路转换缓冲translation lookaside buffer,简称TLB),是更小的cache,使得虚拟-物理地址转换更快。TLB的效率会随页表页数量的增加而下降。第19章描述得更为详细。

本应该为应用提供大内存需求的持久内存系统,很可能遇到大页表问题和TLB的低效率。此场景下使用大页尺寸可以帮助减少页表中的条目数量。当使用巨页尺寸时的主要权衡是每次分配的较高管理费用和内存碎片之间的平衡。在持久内存上使用巨页前,你必须知道应用的行为。频繁分配/释放的应用不适于巨页优化。内存碎片问题可以通过持久内存系统的大地址空间有一定程度的缓解。

15.5  本章小结

持久内存系统的分析和性能优化技术与无持久内存系统的那些技术类似。本章概述了理解性能的一些重要概念。也提供了指导用于刻画已存在无持久内存应用的特征和理解是否适用于持久内存。最后,提出了运行在持久内存平台上应用性能分析和调优的重要度量指标,包含一些例子,如何使用VTune Profiler来收集数据。

性能分析和优化是不断迭代的过程,当你确定下一次优化的投入大于收益时,就应该结束了。使用本章介绍的概念来理解你的负载如何从持久内存获得收益,并使用我们讨论的优化技术来优化这类平台。

作者: charlie_chen

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

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

联系我们

联系我们

022-XXXXXXXX

在线咨询: QQ交谈

邮箱: 1549889473@qq.com

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

微信扫一扫关注我们

关注微博
返回顶部