通过 ABAP 概要分析测量运行时消耗

学习目标

  • 对 ABAP 程序进行概要分析。

  • 使用 ABAP Profiling 检测顺序读取。

ABAP 分析器工具

ABAP 分析器是已集成到 ABAP 开发工具 (ADT) 中的 ABAP 跟踪工具。它可用于所有可执行的 ABAP 代码,例如全局类或 ABAP 单元测试。

从 ABAP 跟踪(ABAP 分析器结果)开始,您可以:

分析性能

  • 识别运行时消耗资源的语句

  • 确定可能想要集中重构工作的性能热点。

  • 比较不同实施的性能,选择最有效的设计

分析程序流

  • 分析应用程序中的调用层次结构

  • 识别模块化单元的过度或不必要的使用

分析数据库访问

  • 识别重复的数据库操作,例如,循环中的 SELECT 语句

  • 识别应用程序访问的数据库对象(表和视图)

ABAP 分析器执行

您可以直接从 ABAP 源代码编辑器或项目浏览器视图中启动单个 ABAP 程序的 ABAP 分析器。观看本课后面的"如何剖析 ABAP 程序并分析结果"演示,了解如何执行此操作。

跟踪参数

使用跟踪参数,可以影响跟踪结果的大小和粒度。如图所示,最重要的设置包括:

聚合设置

在此部分中,指定分析器如何处理重复执行,例如,重复调用同一方法。分析器聚合越多,存储跟踪结果所需的空间就越少。作为一种缺点,某些分析工具不可用于聚合跟踪结果。

您可以从三个选项中进行选择:

  • 无聚合,最大文件大小,所有工具均可用。

  • 部分聚合(按调用语句)、中等文件大小、调用序列不可用。

  • 完全聚合、最小文件大小、聚合调用树不可用。

语句粒度

在此部分中,您可以指定测量的粒度。您可以从三个常用预设中进行选择,或者通过选择自定义语句在详细信息下输入您自己的选择。

同样,您的选择会影响跟踪结果的文件大小和信息值。

存在以下预设:

  • 仅过程单元 – 较小的文件大小,所有语句的运行时都归因于周围的过程单元(方法)。

  • 过程单元、SQL - 中等文件大小、数据库访问的运行时单独跟踪,而不是归因于周围过程单元。

  • 过程单元、SQL、内部表 - 较大的文件大小、数据库访问的运行时以及内部表上的操作单独跟踪,而不是归因于周围过程单元。

开始选项

在此部分中,您可以选择何时开始跟踪。可选择下列选项:

  • 立即 - 将跟踪整个程序执行。

  • 显式开关打开和关闭 - 程序执行从跟踪关闭开始。当程序到达断点时,可以从断点的上下文菜单中选择打开/关闭 ABAP 跟踪。

ABAP 概要分析透视图

剖析 ABAP 程序最好在 ABAP 概要分析透视图中完成。观看本课后面的"如何剖析 ABAP 程序并分析结果"演示,了解如何执行此操作。

在标准配置中,概要分析透视图由三部分组成:

编辑器区域

编辑器区域与 ABAP 透视图和调试器透视图中的编辑器区域相同。打开跟踪结果时,结果将显示在新标签上的此区域中。

跟踪范围

缺省情况下,此区域包含以下视图:

ABAP 跟踪视图显示按项目组织并按用户过滤的跟踪结果。

在 ABAP 跟踪请求视图中,您可以管理请求并创建新请求。

详细信息区域

缺省情况下,此区域包含 属性 视图。如果在编辑器区域中焦点位于资源库对象,属性视图将显示此资源库对象的元数据。

分析跟踪结果时,属性视图用于显示运行时度量的属性。

跟踪概览

双击 ABAP 跟踪视图中的条目时,ABAP 跟踪结果将在编辑器区域的新标签上打开。

缺省情况下,焦点位于概览标签。这是进一步分析的起点。跟踪概览由以下部分组成:

一般信息

在常规信息部分,您可以找到有关此跟踪结果的管理信息:

何时创建跟踪?谁创建的?使用哪个聚合设置?等等。

运行时分配

运行时分配为您提供有关跟踪的运行时如何在以下 ABAP 组件之间分配的信息。

  • ABAP - 处理 ABAP 代码所花费的运行时间,不包括 ABAP 系统程序。

  • 数据库 – 在数据库中执行数据库操作所花费的运行时间。

  • 系统 - 在系统程序中处理 ABAP 代码所花费的运行时间。

分析工具

本部分为您提供集成到跟踪结果显示中的分析工具的简短描述。选择其中一个链接以导航到相应工具。或者,您可以使用概览显示下的标签进行导航。

ABAP 跟踪分析工具

压缩命中清单

通过压缩命中清单,您可以查找最昂贵的程序单元,例如方法。

此清单显示跟踪事件的运行时度量。它被称为压缩,因为它仅限于程序单元。此处未列出数据库访问和其他跟踪事件。

提示

如果您对所有跟踪事件感兴趣,请参考 搜索结果清单 。

该清单以汇总形式显示数据。每个程序单元只出现一次,无论调用频率和调用地点有多少。执行和调用者 列显示此单位的总执行数以及可区分的直接调用位置数。

总时间列显示从调用开始到完成之间的运行时量(以微秒为单位)。如果该单位被多次调用,则该列显示单个调用的总和。总时间 允许您查看过程调用的总贡献。在该示例中,程序花费了总计 15,485 微秒的执行方法 MAIN() 和总计 3,451 微秒的执行方法 GET_FLIGHTS() 。

列自有时间(指定和未指定的 ABAP 语句)以微秒为单位显示运行时量,而不显示等待其他过程单元所花费的时间。我们来详细了解一下方法 MAIN( )。尽管总时间为 15,485 微秒,但此列中的值仅为 18 微秒。这意味着该方法本身几乎没有任何处理,但花费了大部分时间等待其调用的方法。

自有时间列显示运行时量(以微秒为单位),而不显示等待所花费的时间 - 不仅针对调用的过程单元,还显示在跟踪参数中指定的 ABAP 语句。在示例中,方法 GT_FLIGHTS() 不调用任何其他方法,因此自有时间(指定和未指定的 ABAP 语句)中的值与总时间相同。但自有时间中的值仅为 4 微秒,因为该方法几乎花费了所有时间等待数据库访问。

聚合调用树

聚合调用树允许您分析调用程序单位的顺序。

如果重复调用过程单元(例如,在循环中),则该调用仅在聚合调用树中出现一次。执行列中的数量显示重复次数。

提示

如果您对未聚合显示感兴趣,请参阅调用顺序。

在此示例中,方法 GET_FLIGHTS() 由方法 MAIN() 调用,但未调用任何其他过程单元。方法 WRITE() 也由方法 MAIN() 调用,并调用方法 EXEC_WRITE(),该方法又调用方法 GET_NAME(),依此类推。

如何剖析 ABAP 程序并分析结果

在此演示中,您将了解如何剖析 ABAP 应用程序并分析结果。

视频:本节含 SAP 官方视频(视频 ID 1_f3tgj2g9),需在 learning.sap.com 在线观看。

时间表工具

通话时间表

呼叫时间表 允许您以图表的形式可视化呼叫顺序和使用的时间。

该图由两部分组成:底部是整个跟踪的概览,顶部是放大的详细信息。左侧工具栏可用于放大或缩小以及移动焦点。

详细信息图上方的标尺指示从左到右的时间。每个条形图显示一个跟踪事件及其结束时间。条形图越宽,该跟踪事件使用的时间就越长。

左键单击一个条可在 Profiling 透视图的"详细信息"部分显示其详细信息。这样,您可以找出特定条表示的跟踪事件。

该图使用颜色区分不同类型的跟踪事件:系统程序以不同阴影显示灰色,为过程单元使用不同的阴影,使用橙色表示数据库访问等。

提示

打开图上任意位置的上下文菜单以显示或隐藏颜色图例。在我们的示例中,我们隐藏了图例以简化图形。

让我们看一个示例:

第一行

最顶部的长灰色条表示运行时分析。

第二行

在第二行中,我们看到运行时分析在调用应用程序的 MAIN() 方法之前执行两次准备数据库访问。

第三行

在第三行中,我们看到 MAIN() 方法调用两个方法,即 GET_FLIGHTS() 方法和 WRITE() 方法。

第四行

第四行显示方法 GET_FLIGHTS() 和 WRITE() 的结构。我们可以看到 GET_FLIGHTS() 不调用任何程序单元(表示此方法的条下方没有绿色),而是执行单个数据库访问(一个橙色条)。另一方面,WRITE() 方法不会执行其他操作,而是调用一个其它方法。此方法由表示 WRITE 方法的条下方的绿色条表示。

调用时间表中的重复数据库访问

呼叫时间表中的图形显示是识别重复数据库访问的一种非常好的方法。

让我们看一个示例:迭代时第四行和第五行提示中的常规、类似组合的模式。橙色明显地表明,不仅是 ABAP 编码,还涉及重复的数据库访问。

数据库访问

数据库访问工具允许您查找已访问的数据库对象,并在数据库访问中识别排名靠前的使用者。这也是识别来自同一数据库表的重复选择的好方法。

数据库访问工具仅显示与数据库相关的跟踪事件。对于重复的数据库访问,将聚合运行时。您可以在执行列中找到执行次数。

在此示例中,访问了两个表:/DMO/AIRPORT 和 /DMO/CONNECTION。从 /DMO/CONNECTION 读取数据的 SELECT 语句耗时 16,525 微秒,占总运行时的 5%,从 /DMO/AIRPORT 读取的 SELECT SINGLE 语句的 40 次执行消耗了 59,894 微秒,占总运行时的 18%。

如何使用 ABAP 概要分析检测顺序选择

在此演示中,您将了解如何分析包含重复 SELECT 语句的程序。

视频:本节含 SAP 官方视频(视频 ID 1_u00mofav),需在 learning.sap.com 在线观看。

使用 ABAP 概要分析检测性能问题

您注意到您的代码需要很长时间才能运行。使用 ABAP 概要分析执行运行时分析并提高代码的性能。

模板:

  • /LRN/CL_S4D401_ATS_UNIT_TEST(全局类)

解决方案:

  • /LRN/CL_S4D401_ATS_PROFILING(全局类)

任务 1: 复制模板(可选)

复制模板类 /LRN/CL_S4D401_ATS_UNIT_TEST。如果已完成上一练习,则可以跳过此任务并继续编辑类 ZCL_##_SOLUTION

步骤

  1. 将类 /LRN/CL_S4D401_ATS_UNIT_TEST 复制到您自己的包中的类(建议名称:ZCL_##_SOLUTION,其中 ## 代表组编号)。

  2. 在项目浏览器视图中,右键单击类 /LRN/CL_S4D401_ATS_UNIT_TEST 以打开上下文菜单。

  3. 从上下文菜单中,选择复制....

  4. 在包字段中输入的名称。在名称字段中,输入名称 ZCL_##_SOLUTION,其中 ## 代表组编号。

  5. 选择下一步

  6. 确认传输请求并选择完成

  7. 激活复制。

  8. Ctrl + F3 激活类。

任务 2: 剖析代码

使用缺省设置通过 ABAP 分析器执行运行时测量。然后显示和分析结果。

步骤

  1. 使用 ABAP 分析器启动运行时测量。

  2. 在编辑器中显示 ABAP 类 ZCL_##_SOLUTION 的源代码。

  3. 右键单击编辑器并选择参数文件作为ABAP 应用程序(控制台)

  4. 跟踪参数对话框中,选择完成而不更改任何设置。

  5. 切换到剖析器透视图。

  6. 选择 窗口透视图打开透视图其他... 。

  7. 选择 ABAP 概要分析,然后选择打开

  8. 等待跟踪完成(等待右下角的绿色进度标识消失)。然后打开跟踪结果。

  9. 在底部的 ABAP 跟踪视图中,右键单击项目并选择刷新

  10. 双击最新的跟踪(这是项目下的最顶层条目)。跟踪将在列表上方的视图中打开。

  11. 分析跟踪概览。

  12. 总体运行时和 ABAP数据库之间的分配显示在 概览 的下半部分。

  13. 分析跟踪详细信息以回答以下问题:

  14. 要查找消耗最多运行时的处理块,请切换到压缩命中清单标签,并按第 8 列降序对显示进行排序,该列标记为自有时间百分比指定和未指定的 ABAP 语句)。

  15. 要查找最消耗资源的语句,请切换到命中清单标签,并按第六列(标记为自有时间百分比)降序排序显示。

  16. 要在调用树中查找最消耗资源的代码,请切换到聚合调用树标签。

  17. 显示调用时间表(未聚合)并放大以可视化重复的 SELECT 语句。

  18. 切换到通话时间表选项卡。

  19. 使用左侧的滑块进行放大,直到看到条纹橙色和白色模式,该模式指示存在以高频率重复的数据库访问。

任务 3: 改进代码

消除对数据库表 /LRN/PASSFLIGHT 的重复 SELECT SINGLE 访问。为此,扩展 lcl_passenger_flight 类的方法 get_flights_by_carrier 中的 SELECT 语句,以读取乘客航班的所有所需数据。缓冲类似表类型的静态属性中的数据(建议名称:flights_buffer)。在类 lcl_passenger_flight 的**构造函数**中,首先尝试从 flights_buffer 中读取数据。仅当操作失败时,才从数据库中读取数据。

步骤

  1. 返回到 ABAP 透视图并编辑类 lcl_passenger_flight 的方法 get_flights_by_carrier 的实施。扩展 SELECT 语句的字段列表以同时读取飞机类型、最大座位和占用座位数、机票价格和货币代码。

  2. 在 eclipse 菜单中,选择 窗口透视图打开透视图其他... 。选择 ABAP 并选择打开。或者,选择 eclipse 窗口右上角的快捷方式图标。

  3. 打开类 lcl_passenger_flight 的方法 get_flights_by_carrier 的实施,然后按如下所示调整代码:

SELECT
FROM /lrn/passflight
FIELDS carrier_id, connection_id, flight_date,
plane_type_id, seats_max, seats_occupied,
price, currency_code
WHERE carrier_id = @i_carrier_id
INTO TABLE @DATA(keys).
  1. 使用快速修复将内嵌声明变量**重命名为 flights_buffer,将**重命名为 flight

  2. 在 SELECT 语句的 INTO 子句中,将光标置于****上,然后按 Ctrl + 1 显示可用快速修复的列表。

  3. 从快速修复列表中,选择重命名键

  4. 当****仍突出显示时,输入新名称 flights_buffer

  5. 在 LOOP AT 语句的 INTO 子句中,将光标置于****上,然后按 Ctrl + 1 显示可用快速修复的列表。

  6. 从快速修复列表中,选择重命名键

  7. **当键仍突出显示时,输入新的航班**名称。

  8. 使用快速修复将局部变量 flights_buffer 转换为(静态)属性。

  9. 在 SELECT 语句的 INTO 子句中,将光标放在 flights_buffer 上,然后按 Ctrl + 1 显示可用快速修复的列表。

  10. 从快速修复列表中,选择 将 flights_buffer 转换为属性

  11. 导航到静态属性 flights_buffer 的定义并完成结构类型 helper_type 的定义。

  12. 将光标置于 flights_buffer 上,然后按 F3。 或者,按住 Ctrl 键并左键单击 flights_buffer

  13. 为结构类型为 helper_type 的组件添加合适的类型。

注意

最简单的方法是使用数据库表定义 /LRN/PASSFLIGHT 的相应字段。

  1. 按如下所示完成类型 helper_type 的定义:

TYPES: BEGIN OF helper_type,
carrier_id     TYPE /lrn/passflight-carrier_id,
connection_id  TYPE /lrn/passflight-connection_id,
flight_date    TYPE /lrn/passflight-flight_date,
plane_type_id  TYPE /lrn/passflight-plane_type_id,
seats_max      TYPE /lrn/passflight-seats_max,
seats_occupied TYPE /lrn/passflight-seats_occupied,
price          TYPE /lrn/passflight-price,
currency_code  TYPE /lrn/passflight-currency_code,
END OF helper_type.
CLASS-DATA: flights_buffer TYPE TABLE OF helper_type.
  1. 使用快速修复将结构类型 helper_type 重命名为 st_flights_buffer

  2. 在 TYPES 语句中,将光标放在 helper_type 上,然后按 Ctrl + 1 显示可用快速修复的列表。

  3. 从快速修复列表中,选择重命名 helper_type

  4. hel**per_type** 仍突出显示,但输入新名称 st_flights_buffer

  5. 调整 **lcl_passenger_flight****构造**函数的实施。在 SELECT SINGLE 语句之前,从内部表 flights_buffer 中读取单个行。使用构造函数的导入参数标识表行。

  6. 在 方法的开头,添加以下代码:

DATA(flight_raw) = flights_buffer[ carrier_id    = i_carrier_id
connection_id = i_connection_id
flight_date   = i_flight_date ].
  1. 调整 SELECT SINGLE 语句的 INTO 子句。重用已声明的结构 flight_raw 并添加附加项 CORRESPONDING FIELDS OF。

  2. 在 SELECT SINGLE 语句的 INTO 子句中,将 @DATA(flight_raw) 替换为 @flight_raw。注意语法错误 "FLIGHT_RAW" 组件 "CONNECTION_ID" 的数据类型与 "SEATS_MAX" 的数据类型不兼容(请参阅长文本)。

  3. 如下调整代码:

SELECT SINGLE
FROM /lrn/passflight
FIELDS plane_type_id, seats_max, seats_occupied,
price, currency_code
WHERE carrier_id    = @i_carrier_id
AND connection_id = @i_connection_id
AND flight_date   = @i_flight_date
INTO CORRESPONDING FIELDS OF @flight_raw.
  1. 通过异常处理将 flights_buffer 的读取访问包围。确保 ABAP 运行时引发异常 CX_SY_ITAB_LINE_NOT_FOUND 时仅从数据库中读取航班数据。

  2. 如下调整代码:

TRY.
DATA(flight_raw) = flights_buffer[ carrier_id    = i_carrier_id
connection_id = i_connection_id
flight_date   = i_flight_date ].
CATCH cx_sy_itab_line_not_found.
SELECT SINGLE
FROM /lrn/passflight
FIELDS plane_type_id, seats_max, seats_occupied,
price, currency_code
WHERE carrier_id    = @i_carrier_id
AND connection_id = @i_connection_id
AND flight_date   = @i_flight_date
INTO CORRESPONDING FIELDS OF @flight_raw.
ENDTRY.
  1. 在 SELECT 语句后调整 IF 条件。确保结构 flight_raw 不为空,而不是探测系统字段 sy-subrc

  2. IF sy-subrc = 0. 替换为 IF flight_raw IS NOT INITIAL.

任务 4: 重新检查代码

激活并测试全局类。以控制台应用形式执行类。重复剖析以测量性能改进。

步骤

  1. 激活全局类并将其作为控制台应用执行。

  2. Ctrl + F3 激活类。

  3. F9 以控制台应用身份执行类。

  4. 使用 ABAP 分析器启动运行时测量。

  5. 右键单击编辑器并选择参数文件作为ABAP 应用程序(控制台)

  6. 跟踪参数对话框中,选择完成而不更改任何设置。

  7. 切换到剖析器透视图并分析跟踪概览。

  8. 选择 窗口透视图打开透视图其他... 。选择 ABAP 概要分析,然后选择打开

  9. ABAP 跟踪视图中,从 ABAP 项目的上下文菜单中选择刷新。或者,可以将光标置于 ABAP 跟踪视图中的任意位置,然后按 F5 重新加载跟踪结果列表。

  10. 双击最新的跟踪(这是项目下的最顶层条目)。

  11. 总体运行时和 ABAP数据库之间的分配显示在 概览 的下半部分。

  12. 分析跟踪详细信息以回答以下问题:

  13. 要查找消耗最多运行时的处理块,请切换到压缩命中清单标签,并按第 8 列降序对显示进行排序(% 自有时间(指定和未指定的 ABAP 语句))

  14. 要查找最消耗资源的语句,请切换到命中清单标签,并按第六列(标记为自有时间百分比)降序排序显示。