- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
跟踪配置文件质量
在使用本地镜像(Native Image)的PGO(Profile-Guided Optimization,配置文件引导优化)时,最具挑战性的一步是为相关工作负载收集配置文件。由于源代码会不断演进,您的应用程序很少是静态不变的。对于源代码的每次更改,经历“构建-插桩镜像”、“收集-配置文件”和“构建-优化镜像”这些步骤有时会非常耗时。本文档针对“随着应用程序源代码随时间变化,我可以使用现有配置文件多长时间?”这个问题提供了一些答案。
方法1:无限期重用配置文件 #
当您构建优化后的应用程序时,本地镜像会尽力利用给定的配置文件。这意味着,即使为应用程序提供了过时的配置文件(甚至是完全不同应用程序的配置文件),也不应阻止本地镜像生成本地可执行文件。
注意:不正确的配置文件可能导致性能比没有配置文件时更差。这是因为不正确的配置文件可能导致编译器将优化资源花费在应用程序的错误元素上,从而降低重要元素的优先级。
尽管如此,对于不断演进的应用程序无限期重用单一配置文件迟早会适得其反。
方法2:定期收集配置文件 #
由于您的应用程序可能会定期更改,因此定期收集新的配置文件是合乎逻辑的。实现这一点的一种方法是设置一个每日Linux cron作业,它使用master分支的最新版本构建应用程序的插桩版本,运行工作负载以收集配置文件,并将生成的iprof文件上传到FTP服务器,其他优化构建可以从该服务器下载。这确保了正在构建的应用程序版本与正在使用的配置文件之间的差异永远不会超过一个固定的时间间隔(本例中为24小时)。
然而,定期收集配置文件会延长计算时间,因此需要平衡其与应用程序更改频率的关系。如果您的应用程序相对稳定且源代码不经常更改,那么可以不那么频繁地重新生成配置文件。需要记住的一些事项:
- 将您的配置文件生成计划与应用程序发布计划对齐,以避免使用过时的配置文件构建应用程序。
- 理想情况下,将生产工作负载转换为可重现的工作负载,将配置文件的收集作为构建的一部分,然后使用始终新鲜的配置文件创建优化的本地可执行文件。
这样,只要您的工作负载执行应用程序中将来在生产环境中执行的相同部分,您就不会冒配置文件过时或不对齐的风险。
方法3:随时间跟踪配置文件质量指标 #
为了更好地了解配置文件的质量,本地镜像提供了两个您在构建优化可执行文件时可以请求的指标:配置文件相关性(profile relevance)和配置文件适用性(profile applicability)。这些指标反映了配置文件与优化可执行文件中出现的方法和类之间的关系。
在(使用相同配置文件的)不同构建之间,这两个指标值的变化表明这些构建中的类和/或方法的集合也发生了变化(在配置文件收集时间和当前构建之间)。
如何获取配置文件质量指标 #
要计算并打印配置文件质量指标,请在构建优化本地可执行文件时传递-H:+PGOPrintProfileQuality
选项。(此选项为实验性。)
让我们考虑在配置文件引导优化的基本用法中介绍的“生命游戏”示例应用程序。
native-image -cp . GameOfLife -o gameoflife-pgo --pgo=gameoflife.iprof -H:+PGOPrintProfileQuality
在构建输出的第5阶段,您应该会看到关于配置文件适用性和配置文件相关性的额外一行。
GraalVM Native Image: Generating 'gameoflife-pgo' (executable)
...
[5/8] Inlining methods... [***] (0.4s @ 0.28GB)
Info: PGO: Profile applicability is 21.74%. Profile relevance is 72.71%.
...
这些指标的绝对值并不能说明太多问题,不应孤立看待。如前所述,这些指标描述了配置文件与应用程序代码之间的关系。如果您更改应用程序并重用配置文件,您应该会看到指标值的变化。
例如,对applyRules()
应用程序方法应用简单的“方法重命名”重构。从配置文件的角度来看,applyRules()
方法已从应用程序的方法集中移除,并引入了一个名为applyGameRules
的新方法。使用相同的配置文件和修改后的应用程序重新运行优化构建会返回以下输出:
native-image -cp . GameOfLife -o gameoflife-pgo --pgo=gameoflife.iprof -H:+PGOPrintProfileQuality
========================================================================================================================
GraalVM Native Image: Generating 'gameoflife-pgo' (executable)...
...
[5/8] Inlining methods... [***] (0.4s @ 0.28GB)
Info: PGO: Profile applicability is 21.67%. Profile relevance is 72.66%. (6.8s @ 0.29GB)
...
回想一下,第一次构建中配置文件适用性为21.74%,现在为21.67%。类似地,第一次构建中配置文件相关性为72.71%,现在为72.66%。代码的微小更改导致了指标值的微小变化,这表明配置文件可能略有过时。
注意:在此示例中,您重命名了一个非常“热”的方法。这种更改很可能导致性能退步,因为配置文件无法应用于“热”方法。在应用程序的“冷”代码中进行类似更改也会导致这些指标的类似下降,但这不应影响性能。请注意,这些指标是衡量提供的配置文件与应用程序中方法集之间关系的度量,绝不是衡量或预测配置文件、应用程序源代码或依赖项的更改可能造成的任何性能影响。这些数字在单次构建中观察时也意义不大或效用很小。它们的效用在于观察在跨构建重用配置文件或为应用程序的相同构建提供不同配置文件时指标的变化。
配置文件质量指标:适用性 #
适用性指标回答以下问题:“此配置文件对应用程序的方法有多适用?”。在应用程序中编译单个方法期间,它会跟踪代码中需要配置文件的位置N有多少,以及配置文件可用的次数S有多少。配置文件适用性指标是比率S / N
,以百分比表示。
这意味着向应用程序(而不是配置文件)添加新代码应导致配置文件适用性降低。这是因为更多的代码意味着更多的配置文件请求,而配置文件可以应用的次数(S)却被更大的总配置文件请求数(N)所除。
注意:期望配置文件适用性达到100%是错误的。一个好的工作负载在几乎所有情况下都会区分应用程序的“热”部分和“冷”部分,并且不会执行某些“冷”部分的代码。因此,配置文件不会包含应用程序“冷”部分(例如,异常处理程序)的任何条目,这些部分在实际工作负载中无论如何也很少执行。100%的适用性将意味着镜像中代码的所有部分都得到了充分的分析,这在实践中几乎从未发生过。
配置文件质量指标:相关性 #
相关性指标旨在回答以下问题:“配置文件内容与应用程序方法在多大程度上匹配?”。加载配置文件时,其所有数据都会与应用程序方法集进行检查,所有与这些方法不匹配的条目都会被删除。例如,如果您从一个类中删除一个方法,但使用的配置文件仍然包含该方法的条目,那么在配置文件加载期间,所有这些条目都将被删除。配置文件相关性是加载期间未被删除的数据的百分比。
这意味着从应用程序中删除代码(而不是从配置文件中删除)应导致配置文件相关性降低,因为配置文件中与新应用程序版本相关的数据百分比减少了。另一方面,向应用程序添加新代码(例如新类或依赖项)不会影响此指标,因为您需要从配置文件中删除的数据量没有改变。
注意:期望构建与用于收集配置文件的应用程序完全相同的优化二进制文件会得到100%的配置文件相关性是错误的。情况并非如此,因为插桩二进制文件和优化二进制文件的方法在细微之处存在差异。例如,插桩二进制文件包含用于收集配置文件数据以及将该数据序列化到文件的代码。此代码是不必要的,因此不存在于优化二进制文件中。以“生命游戏”为例,相关性在70%左右,主要是因为应用程序非常小(一个不到120行代码的Java类)。因此,插桩二进制文件和优化二进制文件的方法集中的差异被夸大了。对于更大的、实际的应用程序,这个百分比通常会更大,但不会是100%。