synprog のパフォーマンスデータを収集したことを確認してください。 これらの作業がまだ済んでいない場合には、synprog サンプルのデータ収集を参照して実行してください。 パフォーマンスデータを収集したら、実験 test.1.er を開きます。
ここでは、パフォーマンスアナライザが再帰的シーケンスでメトリックを関数に割り当てる方式を紹介します。再帰の効果では、直接的再帰関数呼び出しと間接的再帰関数呼び出しの両方の再帰シーケンスにおいて、呼び出し元がどのように時間の消費に寄与するかについて調べます。 標本コレクタが収集したデータ内では関数呼び出しの各インスタンスが記録されますが、解析では特定の関数のすべてのインスタンスのメトリックが集められます。 synprog プログラムには、再帰呼び出しシーケンスの例として次の 2 つが用意されています。
- 関数 recurse() は、直接的な再帰を示します。 recurse() が real_recurse() を呼び出すと、real_recurse() はテスト条件が満たされるまで自分自身を呼び出します。 テスト条件が満たされた時点で、この関数はユーザー CPU 時間を消費する何らかの処理を行います。その後、制御が最終的に recurse() に達するまで、real_recurse() の連続的呼び出しによって制御フローが戻ります。
- 関数 bounce() は、間接的な再帰を示します。 この関数は bounce_a() という関数を呼び出し、テスト条件が満たされているかどうかをチェックします。 テスト条件が満たされていないと、bounce_b() を呼び出します。 呼び出された bounce_b() は、bounce_a() を呼び出します。 このシーケンスは、bounce_a() でテスト条件が満たされるまで続きます。 bounce_a() でテスト条件が満たされると、bounce_a() はユーザー CPU 時間を消費する何らかの処理を行います。その後、最終的に制御が bounce() に達するまで、bounce_b() と bounce_a() の連続呼び出しによって制御が戻ります。
いずれの場合も、排他的メトリックは実際に処理が実行される関数に属します。この例で実際に処理が実行される関数は、real_recurse() と bounce_a() です。 これらの排他的メトリックは、最終的な関数を呼び出すあらゆる関数に包括的メトリックとして渡されます。
- 「関数」タブで関数 recurse() 検索し、選択します。
関数リストをスクロールする代わりに「検索」ツールを利用できます。
recurse() 関数には包括的ユーザー CPU 時間が表示されますが、recurse() は real_recurse() の呼び出し以外に何も行わないため、その排他ユーザー CPU 時間はゼロになっています。
注: プロファイル実験が統計的な性質をもっており、synprog 上で行う実験が recurse() 関数の消費したごく短い時間を記録している可能性があるため、小さな排他的 CPU 時間値が recurse() に表示されることがあります。 しかし、このようなイベントに起因する排他時間は、包括時間に比べるとごくわずかです。
- 選択した関数 ComputeA() が「呼び出し元-呼び出し先」タブの中央区画に表示され、その呼び出し元が上部区画に表示されます。
選択した関数 recurse() が中央区画に表示されます。 recurse() によって呼び出された関数 real_recurse() は、下部区画に表示されます。 この区画は、「呼び出し先」区画と呼ばれます。
- real_recurse() をクリックします。
「呼び出し元-呼び出し先」タブに、real_recurse() に関する次の情報が表示されます。
- recurse() と real_recurse() は両方とも、real_recurse() の呼び出し元として「呼び出し元」区画 (上部区画) に表示されます。これは、recurse() が real_recurse() を呼び出したあと、real_recurse() が自分自身を再帰的に呼び出すためです。
- real_recurse() は自分自身を呼び出すので、「呼び出し先」区画にも表示されます。
- 実際にユーザー CPU 時間が消費される real_recurse() に対しては、排他的メトリックと包括的メトリックが表示されます。 排他的メトリックは、包括的メトリックとして recurse() に渡されます。
- 呼び出し先の属性メトリックは、呼び出しの再帰特性に影響を受けます。 非再帰呼び出しシーケンスでは、個々の呼び出し先のすべての包括的メトリックは呼び出し元に起因します。 この場合、real_recurse() はそれ自体の呼び出し元と呼び出し先であると同時にリーフ関数でもあります。 属性メトリックの重複カウントを防ぐため、呼び出し先インスタンスは起因時間を示しません。 呼び出し先としての real_recurse() の状態から呼び出しシーケンスに関する情報が得られますが、起因時間に関する情報は得られません。
- 同様に、呼び出し元の属性メトリックは呼び出しの再帰特性に影響を受けます。 real_recurse() で使用される包括時間は、呼び出し元 recurse() (real_recurse() の最終的な呼び出し元) に起因するのではなく、 排他的時間が費やされる real_recurse() のインスタンスの呼び出し元に起因するものです。 recurse() の属性メトリックを調べるには、この関数を選択する必要があります。
- 「関数」タブで関数 bounce() を検索し、選択します。
bounce() 関数には包括的ユーザー CPU 時間が表示されますが、その排他的ユーザー CPU 時間はゼロです。 これは、bounce() が bounce_a() の呼び出し以外には何も行わないからです。
- 選択した関数 ComputeA() が「呼び出し元-呼び出し先」タブの中央区画に表示され、その呼び出し元が上部区画に表示されます。
「呼び出し元-呼び出し先」タブには、bounce() が呼び出す関数は bounce_a() だけであることが表示されます。
- bounce_a() をクリックします。
「呼び出し元-呼び出し先」タブに、bounce_a() に関する次の情報が表示されます。
- bounce() と bounce_b() は両方とも、bounce_a() の呼び出し元として、「呼び出し元」区画に表示されます。
- bounce_b() は、bounce_a() によって呼び出されるので、「呼び出し先」区画にも表示されます。
- 実際にユーザー CPU 時間が消費される bounce_a() に対しては、排他的メトリックと包括的メトリックが表示されます。 これらのメトリックは、bounce_a() を呼び出す関数に包括的メトリックとして渡されます。
- bounce_b() 内の包括時間はすべて bounce_a() の呼び出しに起因しますが、bounce() 内の包括時間はどれも bounce_a() の呼び出しに起因しません。 bounce() と bounce_b() 内の包括時間は個別のものではなく、実際のところ 2 度示されているだけの同じ時間です。 起因時間の重複カウントを避けるため、起因時間は関数の直接の呼び出し元 (ここで時間が消費される) だけで示されています。 この状況は、recurse() と real_recurse() の場合も同じです。
- bounce_a() は中間的な関数であると同時にリーフ関数でもあるため、呼び出し先としての bounce_b() の時間は bounce_a() に起因しません。これは、起因時間の重複カウントを避けるためです。
- bounce_b() をクリックします。
「呼び出し元-呼び出し先」タブに、bounce_b() に関する情報が表示されます。 bounce_a() は、「呼び出し元」区画と「呼び出し先」区画の両方に表示されます。 呼び出し先の起因時間は正しく示されます。これは、bounce_b() がリーフ関数ではなく、作業が行なわれる bounce_a() のインスタンスの呼び出し以降の包括時間を累積するためです。
著作権と商標について