15 パフォーマンスに関する考慮事項

DTraceにより、システムで付加的な作業が発生します。したがって、DTraceを有効にすると、常にシステム・パフォーマンスになんらかの影響が発生します。通常この影響はごくわずかですが、重要な有効化を伴う多くのプローブを有効化した場合は、この影響を無視できないことがあります。この章では、DTraceのパフォーマンスへの影響を最小限に抑えるテクニックをいくつか紹介します。

有効にするプローブの制限

動的なインストゥルメンテーション技術により、DTraceでは、カーネルや任意のユーザー・プロセスについて、広範囲のトレース対象が提供されています。この対象範囲の広さによって、システム動作に対する洞察は飛躍的に向上しますが、プローブの影響が非常に大きくなる場合があります。何万、何十万という単位でプローブを有効にした場合、システムに多大な影響が出るのは明らかです。したがって、問題解決に必要最低限のプローブのみを有効にしてください。たとえば、より小規模な有効化で問題が解決できる場合に、すべてのsyscallプローブを有効にすることは避けるようにします。特定のモジュールや特定の関数に注目する必要がある可能性があります。

注意:

pidプロバイダを使用する場合は、特に注意してください。pidプロバイダはすべての命令をインストゥルメントできるため、アプリケーションで数百万のプローブを有効にでき、それによってターゲット・プロセスが遅くなることがあります。

DTraceは、問題に答えるために多数のプローブを有効にする必要がある状況でも使用できます。多数のプローブを有効にするとシステムの処理速度が大幅に低減しますが、それが原因で致命的な障害がシステムに発生するわけではありません。必要な場合には、多数のプローブを有効にしても問題はありません。

集積体の使用

「集積体」で説明したように、DTraceの集積体を使用するとデータをスケーラブルに集積できます。連想配列の機能は集積体と似ているようでも、汎用変数は本質的にグローバルであるため、連想配列では集積体の線形的なスケーラビリティを提供できません。したがって、可能なかぎり連想配列ではなく集積体を使用することをお薦めします。たとえば、次のDプログラムは連想配列を使用してデータを集計しています。

syscall:::entry
{
  totals[execname]++;
}

syscall::rexit:entry
{
  printf("%40s %d\n", execname, totals[execname]);
  totals[execname] = 0;
}

一方、次のDプログラムでは、同じ結果を得るために集積体を使用し、より望ましい形になっています。

syscall:::entry
{
  @totals[execname] = count();
}

END
{
  printa("%40s %@d\n", @totals);
}

キャッシュ可能な述語の使用

DTraceの述語を使用すると、指定した条件が満たされた場合にのみデータをトレースするようにして、計測結果から不要なデータを排除できます。多数のプローブを有効にするときは通常、/self->traceme/または/pid == 12345/のように、特定のスレッド(複数も可)を識別する形式の述語を使用します。これらの述語の多くは、ほとんどのプローブのほとんどのスレッドに対してfalseを返しますが、プローブの数が多すぎると、この評価自体にかなりのコストがかかります。このコストを抑えるために、DTraceは、スレッド・ローカルの変数(/self->traceme/など)のみが含まれる場合、または不変変数(/pid == 12345/など)の場合に、述語の評価をキャッシュに格納します。キャッシュに入れられた述語を評価するコストは、キャッシュに入っていない述語を評価するときよりはるかに低くて済みます。特に、その述語にスレッド・ローカル変数や文字列比較など比較的コストの高い操作が含まれている場合はその傾向があります。述語のキャッシュはユーザーに意識されませんが、最適な述語を作成するためのいくつかの指針が必要です。最適な述語を作成するためのいくつかの指針を次の表で説明します。

キャッシュ可能 キャッシュ不可

self->mumble

mumblecurthread

mumblepid

tid

execname

curpsinfo->pr_fname

((struct task_struct *)curthread)->comm

pid

curpsinfo->pr_pid

((struct task_struct *)curthread)->pid

tid

curlwpsinfo->pr_lwpid

((struct task_struct *)curthread)->pid

curthread

curthread->any_member

curlwpsinfo->any_member

curpsinfo->any_member

次の例は、述語に連想配列を使用しており、キャッシュ可能ではありません。

syscall::read:entry
{
  follow[pid, tid] = 1;
}

lockstat:::
/follow[pid, tid]/
{}

syscall::read:return
/follow[pid, tid]/
{
  follow[pid, tid] = 0;
}

次の例に従って、キャッシュ可能なスレッド・ローカル変数を使用することをお薦めします。

syscall::read:entry
{
  self->follow = 1;
}

lockstat:::
/self->follow/
{}

syscall::read:return
/self->follow/
{
  self->follow = 0;
}

述語をキャッシュ可能にするには、キャッシュ可能な式でのみ構成する必要があります。次の述語はすべてキャッシュ可能です。

/execname == "myprogram"/

/execname == $$1/

/pid == 12345/

/pid == $1/

/self->traceme == 1/

次の例はグローバル変数を使用しており、キャッシュ可能ではありません。

/execname == one_to_watch/

/traceme[execname]/

/pid == pid_i_care_about/

/self->traceme == my_global/