8

Delphi Sampling Profilerを使用して、アプリケーションの一部をプロファイリングしました。ほとんどの人と同じように、私はほとんどの時間を室内で過ごしていntdll.dllます。

注:時間を無視する オプションをオンにし、Application.Idleから呼び出しますSystem.pasntdllアプリケーションがアイドル状態であるため、内部にはありません。

代替テキスト

複数回実行した後、ほとんどの時間は 内ntdll.dllで費やされているように見えますが、奇妙なことに呼び出し元は次のようになります。

ここに画像の説明を入力

呼び出し元は、仮想ツリービューからのものです:

PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);    

注:ntdll.dllアプリケーションがアイドル状態であるため、アプリケーションは内部にありませんApplication.Idle

私を混乱させているのは、この行自体(つまり、PrepareCellntdllの何かではない) が呼び出し元であるということです。さらに紛らわしいのは、次のことです。

  • だけでなく、それは内部の何かではありません PrepareCell()
  • 呼び出し元であるセットアップ(スタック変数のポップ、暗黙的な例外フレームのセットアップなど)でさえありません。これらは、 PrepareCell 内PrepareCellのホットスポットとしてプロファイラーに表示されます。begin

VirtualTrees.pas:

procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, MaxWidth: Integer);
begin
   ...
end;

だから私はこの行がどのように理解しようとしています:

PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);    

を呼び出してntdll.dllいます。


唯一の他の方法は、次の 3 つのパラメーターです。

  • PaintInfo
  • Window.Left
  • NodeBitmap.Width

おそらくそれらの 1 つは、 を呼び出す関数またはプロパティ ゲッターntdllです。そのため、行にブレークポイントを設定し、実行時に CPU ウィンドウを確認します。

代替テキスト

犯人である可能性のある行があります

call dword ptr [edx+$2c]

しかし、私がそのジャンプをたどると、それは終わらないntdll.dllが、TBitmap.GetWidth:

代替テキスト

ご覧のとおり、これはどこにも呼び出されません。と確かにではありませんntdll.dll


それで、行はどうですか:

PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);    

への呼び出しntdll.dll


注:実際には ntdll.dll を呼び出しているわけではないことは十分承知しています。したがって、有効な回答には、「Sampling Profiler is misleading. that line is not calling into ntdll.dll.」という言葉を含める必要があります。また、答えは、ほとんどの時間がntdll.dll に費やされていないか、強調表示された行が呼び出し元ではないことを示す必要があります。最後に、Sampling Profiler が間違っている理由と、それを修正する方法を説明する必要があります。

更新 2

ntdll.dll とは? Ntdll は、Windows NT のネイティブ API セットです。Win32 API は、 Windows 1/2/3/9x に存在していた Windows API にたラッパーntdll.dllです。実際に ntdll にアクセスするには、直接的または間接的に ntdll を使用する関数を呼び出す必要があります。

たとえば、Delphi アプリケーションがアイドル状態になると、user32.dll 関数を呼び出してメッセージを待機します。

WaitMessage;

実際に見たときは次のとおりです。

USER32.WaitMessage
  mov eax,$00001226
  mov edx,$7ffe0300
  call dword ptr [edx]
  ret

で指定された関数を呼び出すこと$7ffe0300は、Windows が Ring0 に移行する方法であり、EAX で指定された FunctionID を呼び出します。この場合、呼び出されるシステム関数は 0x1226 です。私のオペレーティング システム、Windows Vista では、0x1226 がシステム関数に対応しますNtUserWaitMessage

ntdll.dll にアクセスするには、次のようにします。それを呼び出します。

元の質問を言葉にしたとき、私は必死に手を振って無回答を避けようとしていました. 非常に具体的で、私が見ている現実を注意深く指摘することで、人々が事実を無視するのを防ぎ、手を振って議論をしようとしていました.


アップデート 3

私は2つのパラメータを変換しました:

PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);

スタック変数に:

_profiler_WindowLeft := Window.Left;
_profiler_NodeBitmapWidth := NodeBitmap.Width;
PrepareCell(PaintInfo, _profiler_WindowLeft, _profiler_NodeBitmapWidth);

ボトルネックがないことを確認するには、

  • Windows.Left、 また
  • Nodebitmap.Width

プロファイラーはまだその行を示しています

PrepareCell(PaintInfo, _profiler_WindowLeft, _profiler_NodeBitmapWidth);

それ自体がボトルネックです。PrepareCell内には何もありません。これは、セルを準備するための呼び出しのセットアップ内、または PrepareCell の開始時の何かであることを意味する必要があります。

VirtualTrees.pas.15746: PrepareCell(PaintInfo, _profiler_WindowLeft, _profiler_NodeBitmapWidth);
   mov eax,[ebp-$54]
   push eax
   mov edx,esi
   mov ecx,[ebp-$50]
   mov eax,[ebp-$04]
   call TBasevirtualTree.PrepareCell

ntdll を呼び出すものは何もありません。PrepareCell 自体のプリアンブル:

VirtualTrees.pas.15746: begin
   push ebp
   mov ebp,esp
   add esp,-$44
   push ebx
   push esi
   push edi
   mov [ebp-$14],ecx
   mov [ebp-$18],edx
   mov [ebp-$1c],eax
   lea esi,[ebp-$1c]
   mov edi,[ebp-$18]

そこには何も呼び出されませんntdll.dll


疑問はまだ残っています:

  • 1 つの変数をスタックにプッシュし、他の 2 つの変数をレジスタにプッシュすることがボトルネックになるのはなぜですか?
  • PrepareCell 自体の内部がボトルネックにならないのはなぜですか?
4

2 に答える 2

3

実は、この問題が、私が独自のサンプリング プロファイラーを作成する主な理由でした:
http://code.google.com/p/asmprofiler/wiki/AsmProfilerSamplingMode

完璧ではないかもしれませんが、試してみてください。それについてどう思うか教えてください。

ところで、ほとんどすべての呼び出しがカーネルへの呼び出し (メモリ要求、ペイント イベントなど) で終了するという事実に関係していると思います。カーネルを呼び出す必要がないのは計算だけです。ほとんどの呼び出しは、カーネルの結果を待って終了します。

ntdll.dll!KiFastSystemCallRet

これは、Process Explorer でスレッド スタック ビューを使用するか、Delphi で確認するか、AsmProfiler の「ライブ ビュー」で StackWalk64 API を使用して確認できます:
http://code.google.com/p/asmprofiler/wiki/ProcessStackViewer

于 2010-04-12T18:14:12.190 に答える
1

そこにはおそらく2つのことが起こっています。

1 つ目は、SamplingProfiler が、Delphi コードから Delphi への有効な呼び出しポイントのように見えるものに遭遇するまで、スタックをたどって呼び出し元を識別することです。

問題は、一部の手順では、再初期化せずに一度に大量のスタックを予約する場合があることです。これにより、誤検知が発生する可能性があります。その場合、唯一の手がかりは、誤検知が最近呼び出されたことです。

2 つ目ntdllローカリゼーションです。これは確かに知られていますが、ntdll はユーザー空間の待機ポイントであり、user197220 として、ntdll は、システム スタッフを呼び出して待機しているほとんどの時間待機することになる場所です。結果のために。

あなたの場合、サンプリング レートを下げない限り、247 ミリ秒の CPU 作業時間を見ています。これらの 247 サンプルが何秒ものリアルタイムで収集された場合、おそらくアイドル状態になる可能性があります。偽陽性は VirtualTree のペイント準備を示しているため、ntdll 時間は実際にはペイント時間 (ドライバーまたは OS ソフトウェア) であると考えられます。実際に描画を行うコードをコメントアウトしてみてください。

于 2010-07-28T13:39:06.820 に答える