Я профилировал часть своего приложения, используя Delphi Sampling Profiler . Как и большинство людей , я вижу большую часть времени, проведенного внутри ntdll.dll
.
Примечание: я включил опции на игнорировать Application.Idle
время и звонки с System.pas
. Так что
не внутри ntdll
, потому что
приложение бездействует:
альтернативный текст http://i40.tinypic.com/fkmc9j.jpg
После нескольких прогонов, несколько раз, большая часть времени, кажется, проводится внутри ntdll.dll
, но странно то, кто звонит:
Звонящий из виртуального дерева:
PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);
Примечание: Приложение не находится внутри ntdll.dll
, потому что
приложение бездействует, потому что
звонящий не Application.Idle
.
Что меня смущает, так это то, что именно эта строка сама (т.е. не что-то внутри PrepareCell) является вызывающей стороной для ntdll
. Еще более запутанным является то, что:
- не только не что-то внутри
PrepareCell()
- это даже не setup из
PrepareCell
(например, извлечение переменных стека, установка неявных фреймов исключений и т. Д.), Которые являются вызывающими. Эти вещи будут отображаться в профилировщике как точка доступа на begin
внутри PrepareCell.
VirtualTrees.pas:
procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX, MaxWidth: Integer);
begin
...
end;
Итак, я пытаюсь понять, как эта строка:
PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);
звонит ntdll.dll
.
Единственными другими способами являются три параметра:
PaintInfo
Window.Left
NodeBitmap.Width
Может быть, одна из них - это функция или метод получения свойства, который вызовет ntdll
. Поэтому я поставил точку останова на линии и посмотрел на окно процессора во время выполнения:
альтернативный текст http://i44.tinypic.com/2ut0pkx.jpg
Там есть строка, которая может быть виновником:
call dword ptr [edx+$2c]
Но когда я следую за этим прыжком, он не заканчивается ntdll.dll
, а TBitmap.GetWidth
:
альтернативный текст http://i44.tinypic.com/2uswzlc.jpg
Который, как вы видите, никуда не звонит; и, конечно, не в ntdll.dll
.
Так как же линия:
PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);
звонит в ntdll.dll
?
Примечание: Я прекрасно знаю, что на самом деле это не вызов ntdll.dll. Поэтому любой правильный ответ должен включать слова «Sampling Profiler вводит в заблуждение; эта строка не вызывает ntdll.dll». Ответ также должен либо сказать, что большую часть времени не тратится в ntdll.dll, либо что выделенная строка не является вызывающей стороной. Наконец, любой ответ должен объяснить, почему Sampling Profiler неправильный и как его можно исправить.
Обновление 2
Что такое ntdll.dll? Ntdll - это собственный набор API для Windows NT. Win32 API - это оболочка вокруг ntdll.dll
, которая выглядит как Windows API, существовавший в Windows 1/2/3 / 9x. Чтобы на самом деле попасть в ntdll, вы должны вызвать функцию, которая использует ntdll прямо или косвенно.
Например, когда мое приложение Delphi бездействует, оно ожидает сообщения, вызывая функцию user32.dll:
WaitMessage;
Когда вы на самом деле смотрите на это:
USER32.WaitMessage
mov eax,$00001226
mov edx,$7ffe0300
call dword ptr [edx]
ret
Вызов функции, указанной в $7ffe0300
, является способом, которым Windows переходит в Ring0, вызывая FunctionID, указанный в EAX. В этом случае вызывается системная функция 0x1226. В моей операционной системе Windows Vista 0x1226 соответствует системной функции NtUserWaitMessage
.
Вот так вы попадаете в ntdll.dll: вы его называете.
я отчаянно пытался избежать махания рукой без ответа, когда я сформулировал исходный вопрос. Будучи очень конкретным, тщательно указывая на реальность того, что я вижу, я пытался помешать людям игнорировать факты и пытался использовать аргумент, размахивающий рукой.
Обновление три
я преобразовал два параметра:
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
.
Все еще остаются вопросы:
- почему перенос одной переменной в стек, а двух других в регистры является узким местом?
- почему внутри самого PrepareCell ничего не узкое место?