コードの分析およびテスト
データ型およびデータ型変換の正しい使用
文字項目の処理
ABAP SQL でのコードプッシュダウンの使用
内部テーブルのパフォーマンスの改善
権限チェックの実装
効果的なオブジェクト指向コードの設計
例外クラスの定義と処理
ABAP コードへの文書の追加

ABAP プロファイリングによる実行時間消費の測定

Objectives

After completing this lesson, you will be able to:
  • ABAP プログラムのプロファイルを作成します。
  • ABAP プロファイリングを使用して順次読込を検出します。

ABAP プロファイラツール

ABAP プロファイラは、ABAP Development Tools (ADT) に統合された ABAP トレースツールです。これは、グローバルクラスや ABAP ユニットテストなど、すべての実行可能な ABAP コードで使用できます。

ABAP トレース (ABAP プロファイラの結果) から開始して、以下を実行することができます。

パフォーマンスの分析

  • 実行時間が長い SQL 文の特定
  • リファクタリング作業を集中させたいパフォーマンスのホットスポットを特定する
  • さまざまな実装のパフォーマンスを比較して、最も効果的な設計を選択する

プログラムフローの分析

  • アプリケーションでコール階層を分析します。
  • モジュール化単位の過剰または不要な使用の特定

データベースアクセスの分析

  • 繰り返されるデータベース操作 (ループ内の SELECT 文など) の特定
  • アプリケーションによってアクセスされるデータベースオブジェクト (テーブルおよびビュー) の特定

ABAP プロファイラの実行

個々の ABAP プログラムの ABAP プロファイラは、ABAP ソースコードエディタまたは Project Explorer ビューから直接起動することができます。これを行う方法については、このレッスンの後のデモ "ABAP プログラムのプロファイリングと結果の分析方法" を参照してください。

トレースパラメータ

トレースパラメータを使用して、トレース結果のサイズとグラニュラリティを制御します。図に示すように、最も重要な設定は以下のとおりです。

集計設定

このセクションでは、プロファイラによる繰返実行 (同じメソッドの繰返呼出など) の処理方法を指定します。プロファイラの集計が多いほど、トレース結果を保存するために必要な領域が少なくなります。一種の欠点として、一部の分析ツールは集計トレース結果で利用できません。

以下の 3 つのオプションから選択することができます。

  • 集約なし、最大ファイルサイズ、すべてのツールが利用可能です。
  • 部分集計 (呼出命令別)、中程度のファイルサイズ、呼出順序は使用できません。
  • フル集約、最小ファイルサイズ、集約コールツリーは使用できません。

文の粒度

このセクションでは、測定の粒度を指定することができます。3 つの一般的なプリセットから選択するか、カスタムステートメントを選択して、詳細で独自の選択を入力することができます。

ここでも、選択はファイルサイズおよびトレース結果の情報値に影響します。

以下のプリセットがあります。

  • 手続ユニットのみ - ファイルサイズが小さいため、すべての文の実行時間は周辺の処理手順ユニット (メソッド) に帰属します。
  • プロシージャユニット、SQL - ファイルサイズ (中)。データベースアクセスの実行時間は個別にトレースされ、周辺の処理手順ユニットに帰属しません。
  • プロシージャユニット、SQL、内部テーブル - 大きいファイルサイズ、データベースアクセスの実行時間、および内部テーブルに対する操作は個別にトレースされ、周辺の処理手順ユニットに帰属しません。

開始オプション

このセクションでは、トレースを開始するタイミングを選択することができます。以下のオプションがあります。

  • 即時 - プログラム実行全体がトレースされます。
  • 明示的なスイッチのオン/オフ - プログラムの実行は、トレースをオフにして開始します。プログラムがブレークポイントに到達したら、ブレークポイントのコンテキストメニューから ABAP トレースオン/オフ切替を選択することができます。

ABAP プロファイリングパースペクティブ

ABAP プログラムのプロファイリングは、ABAP Profiling パースペクティブで行うのが最適です。これを行う方法については、このレッスンの後のデモ "ABAP プログラムのプロファイリングと結果の分析方法" を参照してください。

標準設定では、Profiling パースペクティブは以下の 3 つの部分で構成されます。

エディタ領域

エディタ領域は、ABAP パースペクティブおよびデバッガパースペクティブのエディタ領域と同じです。トレース結果を開くと、この領域の新しいタブに表示されます。

トレース領域

デフォルトでは、この領域は以下のビューで構成されています。

ABAP トレースビューには、トレース結果がプロジェクト別に分類され、ユーザによってフィルタリングされて表示されます。

ABAP トレース要求ビューでは、要求を管理し、新しい要求を作成することができます。

Details 領域

デフォルトでは、この領域には Properties ビューがあります。エディタ領域でフォーカスがリポジトリオブジェクトにある場合、プロパティビューにはこのリポジトリオブジェクトのメタデータが表示されます。

トレース結果の分析時には、プロパティビューを使用して実行時間測定のプロパティが表示されます。

トレース概要

ABAP トレースビューでエントリをダブルクリックすると、エディタ領域の新しいタブで ABAP トレース結果が開きます。

デフォルトでは、フォーカスは概要タブにあります。これは、追加分析の開始ポイントです。トレース概要は、以下の部分で構成されています。

一般情報:

一般情報セクションには、このトレース結果に関する管理情報が表示されます。

トレースはいつ作成されましたか。誰がそれを作成しましたか? どの集計設定を使用しますか。など

実行時間配分

実行時間配分では、トレースされたランタイムが以下の ABAP コンポーネントに分散された方法に関する情報が提供されます。

  • ABAP - ABAP システムプログラムを含まない ABAP コードの処理にかかった実行時間。
  • データベース - データベース操作の実行に費やされた実行時間。
  • System - システムプログラムでの ABAP コードの処理にかかった実行時間。

分析ツール

このセクションでは、トレース結果表示に統合された分析ツールについて簡単に説明します。リンクのいずれかを選択して、各ツールにナビゲートします。または、概要表示の下にあるタブを使用してナビゲートすることもできます。

ABAP トレース分析ツール

圧縮該当対象一覧

圧縮該当対象一覧を使用すると、最も負荷の高い手続単位 (メソッドなど) を検索することができます。

この一覧には、トレースイベントの実行時間測定が表示されます。自らを手続ユニットに制限するため、凝縮と呼ばれる。データベースアクセスおよびその他のトレースイベントは、ここには一覧表示されません。

ヒント

すべてのトレースイベントに関心がある場合は、代わりに該当対象一覧を参照してください。

一覧には、データが集計形式で表示されます。各手続ユニットは、呼び出された頻度や異なる場所の数に関係なく、1 回だけ出現します。列実行および呼出元には、このユニットの全体的な実行数および区別可能な直接コールポジションの数が表示されます。

合計時間列には、呼出の開始から終了までの実行時間がマイクロ秒単位で表示されます。ユニットが複数回呼び出された場合、この列には個々の呼出の合計が表示されます。合計時間 (Total Time) では、プロシージャコールの貢献度合計を確認できます。この例では、プログラムで、メソッド MAIN( ) の実行に合計 15,485 µs、合計で 3,451 µs の実行メソッド GET_FLIGHTS( ) を使用しました。

列ユーザ時間 (指定済および未指定 ABAP 命令) には、他のプロシージャユニットの待機に費やされた時間なしで、実行時間がマイクロ秒単位で表示されます。メソッド MAIN( ) について詳しく見てみましょう。合計時間は 15,485 µs ですが、この列の値は 18 µs のみです。つまり、メソッド自体の処理はほとんど行われず、呼び出されたメソッドを待機する時間がほとんどかかりました。

列ユーザ時間には、呼び出されたプロシージャユニットだけでなく、トレースパラメータで指定された ABAP 命令についても、待機に費やされた時間なしの実行時間 (マイクロ秒) が表示されます。この例では、メソッド GT_FLIGHTS( ) は他のメソッドを呼び出さないため、固有時刻 (指定済および未指定 ABAP 命令) の値は合計時間と同じです。しかし、メソッドはデータベースアクセスを待機する時間をほとんどすべて費やしたため、Own Time の値は 4 µs にすぎません。

集約コールツリー

集約コールツリーを使用すると、手続ユニットが呼び出された順序を分析することができます。

プロシージャユニットの呼出が (ループなどで) 繰り返される場合、呼出は集約コールツリーに 1 回だけ表示されます。列実行の数には、繰返の数が表示されます。

ヒント

未集約表示に関心がある場合は、代わりに呼出順序を参照してください。

この例では、メソッド GET_FLIGHTS( ) がメソッド MAIN( ) によって呼び出されましたが、他の手続ユニットは呼び出されませんでした。メソッド WRITE( ) はメソッド MAIN( ) によっても呼び出され、メソッド EXEC_WRITE( ) が呼び出され、メソッド EXEC_WRITE( ) はメソッド GET_NAME( ) などを呼び出しました。

ABAP プログラムのプロファイリング方法および結果の分析方法

このデモでは、ABAP アプリケーションをプロファイリングし、結果を分析する方法を確認します。

タイムラインツール

コールタイムライン

コールタイムラインでは、消費されたコール順序と時間をダイアグラムの形式で視覚化することができます。

このダイアグラムは、下部にあるトレース全体の概要と、上部に拡大された詳細の 2 つの部分で構成されています。左側のツールバーでは、拡大/縮小したり、フォーカスを移動したりすることができます。

詳細図の上にあるルーラーは、左から右へ進む時間を示す。各バーには、1 つのトレースイベントとその開始時刻が表示されます。棒が広いほど、そのトレースイベントによって消費された時間が多くなります。

バーを左クリックすると、Profiling パースペクティブの詳細セクションに詳細が表示されます。これにより、特定のバーが表すトレースイベントを見つけることができます。

この図では、色を使用してさまざまなタイプのトレースイベント (システムプログラムでは異なる影で灰色、手順ユニットでは異なる影で緑色、データベースアクセスにはオレンジ色など) を区別しています。

ヒント

色凡例の表示/非表示を切り替えるには、ダイアグラムの任意の場所でコンテキストメニューを開きます。この例では、凡例を非表示にしてグラフィックをシンプルにしています。

例を見てみましょう。

最初の行

最上部にある灰色の長いバーは、実行時間分析を表します。

2 行目

2 行目では、アプリケーションのメソッド MAIN( ) が呼び出される前に、実行時間分析によって 2 つの準備データベースアクセスが実行されていることがわかります。

3 行目

3 行目では、MAIN( ) メソッドは GET_FLIGHTS( ) メソッドと WRITE( ) メソッドの 2 つのメソッドを呼び出しています。

4 行目

4 行目は、メソッド GET_FLIGHTS( ) および WRITE( ) の構造を示します。GET_FLIGHTS( ) では、プロシージャユニットは呼び出されず (このメソッドを表すバーの下に緑色なし)、単一のデータベースアクセス (オレンジ色のバー 1 つ) が実行されます。一方、WRITE( ) メソッドは、他の 1 つのメソッドを呼び出す以外は何もしません。このメソッドは、WRITE メソッドを表すバーの下の緑色のバーで表されます。

呼出時系列での繰返データベースアクセス

コールタイムラインのグラフィック表示は、繰り返されるデータベースアクセスを特定するのに非常に適した方法です。

例を見てみましょう。4 行目の通常の組合せのようなパターンと、5 行目のパターンは反復をヒントとします。オレンジ色は、ABAP コーディングではなく、データベースアクセスの繰返を伴うことを明らかにします。

データベースアクセス

データベースアクセスツールを使用すると、アクセスされたデータベースオブジェクトを検索し、データベースアクセスの中で上位のコンシューマを特定することができます。また、同じデータベーステーブルから繰り返される選択を特定する方法も適しています。

データベースアクセスツールでは、データベースに関連するトレースイベントのみが表示されます。繰り返されるデータベースアクセスでは、実行時間が集約されます。実行数は、列実行に表示されます。

この例では、 /DMO/AIRPORT と /DMO/CONNECTION の 2 つのテーブルにアクセスしています。/DMO/CONNECTION からデータを読み込む 1 つの SELECT 文には 16,525 µs かかり、これは全体の実行時間の 5% であり、/DMO/AIRPORT からの SELECT SINGLE 命令の読込の 40 回の実行で 59,894 µs、または合計実行時間の 18 % が消費されました。

ABAP プロファイリングを使用した順次選択の検出方法

このデモでは、繰り返される SELECT 命令を含むプログラムの分析方法を確認します。

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、## はグループ番号)。

    1. Project Explorer ビューで、クラス /LRN/CL_S4D401_ATS_UNIT_TEST を右クリックしてコンテキストメニューを開きます。

    2. コンテキストメニューから Duplicate.... を選択します。

    3. Package 項目にパッケージの名称を入力します。名称項目に、名称 ZCL_##_SOLUTION (## はグループ番号) を入力します。

    4. Next を選択します。

    5. 移送依頼を確認し、Finish を選択します。

  2. コピーを有効化します。

    1. Ctrl + F3 を押してクラスを有効化します。

タスク 2: コードのプロファイリング

デフォルト設定を使用して、ABAP プロファイラで実行時測定を実行します。次に、結果を照会して分析します。

ステップ

  1. ABAP プロファイラで実行時測定を開始します。

    1. エディタで ABAP クラス ZCL_##_SOLUTION のソースコードを照会します。

    2. エディタを右クリックし、Profile AsABAP Application (Console) を選択します。

    3. トレースパラメータダイアログボックスで、設定を変更せずに終了を選択します。

  2. プロファイラパースペクティブに切り替えます。

    1. WindowPerspectiveOpen PerspectiveOther... を選択します。

    2. ABAP Profiling を選択し、Open を選択します。

  3. トレースが終了するまで待機します (右下隅の緑色の進捗インジケータが表示されなくなるまで待機します)。次に、トレース結果を開きます。

    1. 下部の ABAP トレースビューで、プロジェクトを右クリックし、リフレッシュを選択します。

    2. 最新のトレースをダブルクリックします (これはプロジェクトの最上部にあるエントリです)。一覧の上にあるビューでトレースが開きます。

  4. トレース概要を分析します。

    1. 全体の実行時間および ABAPデータベース間の配分が概要の下部セクションに表示されます。

  5. トレース詳細を分析して、以下の質問に答えてください。

    1. 最も実行時間が消費された処理ブロックを見つけるには、圧縮該当対象一覧タブに切り替えて、 % Own Time (Specified and Unspecified ABAP Statements) というラベルの 8 列目の降順で表示をソートします。

    2. 最も負荷の高い SQL 文を検索するには、該当対象一覧タブに切り替えて、表示を % ユーザ時間というラベルの 6 列目の降順でソートします。

    3. 呼出ツリー内で最も負荷の高いコードを見つけるには、Aggregated Call Tree タブに切り替えます。

  6. コールタイムライン (非集計) を表示し、拡大して繰り返される SELECT 文を視覚化します。

    1. コール時系列タブに切り替えます。

    2. 左側のスライダを使用して、高い頻度で繰り返されるデータベースアクセスの存在を示すストライプのオレンジと白のパターンが表示されるまで拡大します。

タスク 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 命令の項目一覧を拡張して、航空機タイプ、最大座席数と予約済座席数、チケット価格、および通貨コードも読み込みます。

    1. Eclipse メニューから、WindowPerspectiveOpen PerspectiveOther... を選択します。ABAP を選択し、Open を選択します。または、Eclipse ウィンドウの右上隅にあるショートカットアイコンを選択します。

    2. クラス lcl_passenger_flight のメソッド get_flights_by_carrier の実装を開き、以下のようにコードを調整します。

      ABAP
      1234567
      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).
  2. クイックフィックスを使用して、インライン宣言された変数キーの名前を flights_buffer に変更し、flightのキーを変更します。

    1. SELECT 命令の INTO 句で、キーにカーソルを合わせて Ctrl + 1 を押すと、利用可能なクイックフィックスの一覧が表示されます。

    2. クイックフィックスの一覧から、キーの名前変更を選択します

    3. キーがまだ強調表示されている間に、新しい名称 flights_buffer を入力します。

    4. LOOP AT 命令の INTO 句で、カーソルをキーに置き、Ctrl + 1 を押して利用可能なクイックフィックスの一覧を表示します。

    5. クイックフィックスの一覧から Rename key を選択します。

    6. キーがまだ強調表示されている間に、新しいフライト名を入力します。

  3. クイックフィックスを使用して、ローカル変数 flights_buffer を (静的) 属性に変換します。

    1. SELECT 文の INTO 句で、flights_buffer にカーソルを置き、Ctrl + 1 を押して利用可能なクイックフィックスの一覧を表示します。

    2. クイックフィックスの一覧から、flights_buffer を属性に変換を選択します。

  4. 静的属性 flights_buffer の定義にナビゲートし、構造タイプ helper_type の定義を完了します。

    1. flights_buffer にカーソルを置き、F3 を押します。

      または、Ctrl キーを押しながら flights_buffer を左クリックします。

    2. 構造タイプ helper_type のコンポーネントに適したデータ型を追加します。

      注記

      最も簡単な方法は、データベーステーブル定義 /LRN/PASSFLIGHT の対応する項目を使用することです。
    3. helper_type 型の定義を次のように設定します。

      ABAP
      1234567891011
      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.
  5. クイックフィックスを使用して、構造タイプ helper_type の名前を st_flights_buffer に変更します。

    1. TYPES 命令で helper_type にカーソルを置き、Ctrl + 1 を押して利用可能なクイックフィックスの一覧を表示します。

    2. クイックフィックスの一覧から、helper_type の名称変更を選択します。

    3. helper_type が強調表示されている間に、新しい名称 st_flights_buffer を入力します。

  6. lcl_passenger_flightコンストラクタの実装を調整します。SELECT SINGLE 命令の前に、内部テーブル flights_buffer から単一行を読み込みます。コンストラクタのインポートパラメータを使用して、テーブル行を識別します。

    1. メソッドの先頭に、以下のコードを追加します。

      ABAP
      123
      DATA(flight_raw) = flights_buffer[ carrier_id = i_carrier_id connection_id = i_connection_id flight_date = i_flight_date ].
  7. SELECT SINGLE 命令の INTO 句を調整します。すでに宣言されている構造 flight_raw を再利用し、CORRESPONDING FIELDS OF オプションを追加します。

    1. SELECT SINGLE 命令の INTO 句で、@DATA(flight_raw)@flight_raw に置き換えます。構文エラーに注意してください。"FLIGHT_RAW" のコンポーネント "CONNECTION_ID" のデータ型は、"SEATS_MAX" のデータ型と互換性がありません (テキスト (長) 参照)。

    2. コードを以下のように調整します。

      ABAP
      12345678
      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.
  8. 例外処理による flights_buffer への読込アクセスを囲みます。ABAP ランタイムで例外 CX_SY_ITAB_LINE_NOT_FOUND が発生した場合にのみ、データベースからフライトデータを読み込むようにしてください。

    1. コードを以下のように調整します。

      ABAP
      1234567891011121314
      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.
  9. SELECT 文の後に IF 条件を調整します。システム項目 sy-subrc を確認するのではなく、構造 flight_raw が空でないことを確認してください。

    1. IF sy-subrc = 0.IF flight_raw IS NOT INITIAL. に置き換えます。

タスク 4: コードを再チェックする

グローバルクラスを有効化してテストします。クラスをコンソールアプリとして実行します。プロファイリングを繰り返して、パフォーマンス改善を測定します。

ステップ

  1. グローバルクラスを有効化し、コンソールアプリとして実行します。

    1. Ctrl + F3 を押してクラスを有効化します。

    2. F9 を押して、クラスをコンソールアプリとして実行します。

  2. ABAP プロファイラで実行時測定を開始します。

    1. エディタを右クリックし、Profile AsABAP Application (Console) を選択します。

    2. トレースパラメータダイアログボックスで、設定を変更せずに終了を選択します。

  3. プロファイラパースペクティブに切り替え、トレース概要を分析します。

    1. WindowPerspectiveOpen PerspectiveOther... を選択します。ABAP Profiling を選択し、Open を選択します。

    2. ABAP トレースビューで、ABAP プロジェクトのコンテキストメニューからリフレッシュを選択します。または、ABAP トレースビューの任意の場所にカーソルを置き、F5 を押してトレース結果の一覧を再ロードすることもできます。

    3. 最新のトレースをダブルクリックします (これはプロジェクトの最上部にあるエントリです)。

    4. 全体の実行時間および ABAPデータベース間の配分が概要の下部セクションに表示されます。

  4. トレース詳細を分析して、以下の質問に答えてください。

    1. 最も実行時間が消費された処理ブロックを見つけるには、圧縮該当対象一覧タブに切り替え、8 列目 (% Own Time (Specified and Unspecified ABAP Statements)) の降順で表示をソートします。

    2. 最も負荷の高い SQL 文を検索するには、該当対象一覧タブに切り替えて、表示を % ユーザ時間というラベルの 6 列目の降順でソートします。