я представил часть своего приложения с помощью Профилировщика Выборки Delphi. Как большинство людей, я вижу большинство времени, проведенного внутри ntdll.dll
.
Примечание: я включил опции проигнорировать
Application.Idle
время и вызовы отSystem.pas
. Таким образом, это не внутриntdll
потому что приложение неактивно:
После нескольких выполнений, многократно, большинство времени, кажется, потрачено внутри ntdll.dll
, но нечетная вещь состоит в том, кто вызывающая сторона:
Вызывающая сторона от Виртуального Treeview:
PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);
Примечание: Приложение не внутри
ntdll.dll
потому что приложение неактивно, потому что вызывающая сторона неApplication.Idle
.
То, что смущает меня, - то, что это - эта строка сама (т.е. не что-то в PrepareCell) вызывающая сторона в ntdll
. Еще более сбивающий с толку то, что:
PrepareCell()
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
. Таким образом, я поместил точку останова на строку и смотрю на окно CPU во времени выполнения:
Существует строка там, которая могла бы быть преступником:
call dword ptr [edx+$2c]
Но когда я следую за тем переходом, он не заканчивается в ntdll.dll
, но TBitmap.GetWidth
:
Который, как Вы видите, не звонит нигде; и конечно не в ntdll.dll
.
Таким образом, как строка:
PrepareCell(PaintInfo, Window.Left, NodeBitmap.Width);
вызов в ntdll.dll
?
Примечание: я знаю очень хорошо, что это действительно не звонит в ntdll.dll. Таким образом, любой действительный ответ должен будет включать слова "Выборка Профилировщика, вводит в заблуждение; та строка не звонит в ntdll.dll". В ответе должно будет также быть или сказано, что большинство времени не потрачено в ntdll.dll, или что выделенная строка не является вызывающей стороной. Наконец любой ответ должен будет объяснить, почему Выборка Профилировщика является неправильной, и как это может быть зафиксировано.
Что такое ntdll.dll? Ntdll является набором встроенного API Windows NT. API Win32 является оберткой вокруг 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
, илиПрофилировщик все еще указывает что строка
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
.
Вопросы все еще остаются:
На самом деле эта проблема была моей основной причиной, по которой я создал свой собственный профилировщик выборки:
http://code.google.com/p/asmprofiler / wiki / AsmProfilerSamplingMode
Возможно, не идеально, но вы можете попробовать. Дай мне знать, что ты думаешь об этом.
Кстати, я думаю, это связано с тем, что почти все вызовы заканчиваются вызовами ядра (запросы памяти, события рисования и т. Д.). Для вызова ядра не требуется только вычисления. Большинство вызовов заканчивается ожиданием результатов ядра:
ntdll.dll!KiFastSystemCallRet
Вы можете увидеть это в Process Explorer с представлением стека потоков, или в Delphi, или с помощью StackWalk64 API в моем " Просмотр в реальном времени "AsmProfiler:
http://code.google.com/p/asmprofiler/wiki/ProcessStackViewer
Вероятно, там происходят две вещи.
Первый заключается в том, что SamplingProfiler идентифицирует вызывающего, поднимаясь по стеку, пока не обнаружит то, что выглядит как действительная точка вызова в Delphi из кода Delphi.
Дело в том, что некоторые процедуры могут одновременно резервировать большой объем стека без его повторной инициализации. Это может привести к ложному срабатыванию. Единственным признаком тогда будет то, что ваше ложное срабатывание было недавно вызвано.
Вторая вещь - это локализация ntdll
, о которой доподлинно известно, однако ntdll - это ваша точка ожидания в пользовательском пространстве, а, как и user197220, ntdll - это то место, где вы закончите ожидание большую часть времени вы вызываете системные вещи и ждете результата.
В вашем случае, если вы не уменьшите частоту дискретизации, вы получите 247 мс рабочего времени процессора, которое, вероятно, могло бы сойти за бездействие, если бы эти 247 выборок были собраны в течение многих секунд реального времени. Поскольку ложные срабатывания указывают на подготовку к отрисовке VirtualTree, я могу поспорить, что время ntdll на самом деле является временем отрисовки (драйвер или программное обеспечение ОС). Вы можете попробовать закомментировать код, который на самом деле рисует, чтобы быть уверенным.