Note: i turned on the options to ignore
Application.Idle
time,and calls fromSystem.pas
. So it
isn’t insidentdll
because the
application is idle:07002
多次运行多次后,大部分时间似乎都花在了ntdll.dll中,但奇怪的是调用者是谁:
来电者来自Virtual Treeview:
PrepareCell(PaintInfo,Window.Left,NodeBitmap.Width);
Note: The application is not inside
ntdll.dll
because the
application is idle,because the
caller isn’tApplication.Idle
.
令我困惑的是,这条线本身(即不是PrepareCell内部的东西)是ntdll的调用者.更令人困惑的是:
>不仅不是PrepareCell内部的东西()
>它甚至不是PrepareCell的设置(例如弹出堆栈变量,设置隐式异常帧等),即调用者.这些东西会在探查器中显示为PrepareCell内部开始的热点.
VirtualTrees.pas:
procedure TBaseVirtualTree.PrepareCell(var PaintInfo: TVTPaintInfo; WindowOrgX,MaxWidth: Integer); begin ... end;
所以我想弄清楚这一行:
PrepareCell(PaintInfo,NodeBitmap.Width);
正在调用ntdll.dll.
其他唯一的方法是三个参数:
> PaintInfo
> Window.Left
> NodeBitmap.Width
也许其中一个是调用ntdll的函数或属性getter.所以我在线上放了一个断点,并在运行时查看cpu窗口:
alt text http://i44.tinypic.com/2ut0pkx.jpg
那里有一条线可能是罪魁祸首:
call dword ptr [edx+$2c]
但是,当我按照跳转,它不会在ntdll.dll中结束,但TBitmap.GetWidth:
alt text http://i44.tinypic.com/2uswzlc.jpg
正如你所看到的那样,你不会在任何地方打电话;当然不会进入ntdll.dll.
那么这条线怎么样:
PrepareCell(PaintInfo,NodeBitmap.Width);
调用ntdll.dll?
注意:我完全知道它并没有真正调用ntdll.dll.所以任何有效的答案都必须包括“采样分析器误导;该行没有调用ntdll.dll”.答案还必须要么说大部分时间都没花在ntdll.dll上,或者突出显示的行不是调用者.最后,任何答案都必须解释为什么Sampling Profiler是错误的,以及如何修复它.
更新2
什么是ntdll.dll文件? Ntdll是Windows NT的本机API集. Win32 API是ntdll.dll的包装器,看起来像Windows 1/2 / 3 / 9x中存在的Windows API.为了实际进入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:你称之为.
当我提出原始问题时,我正拼命地试图避免挥手无法回答.通过非常具体,仔细地指出我所看到的现实,我试图阻止人们忽视事实,并试图用挥手的论点.
更新三
我转换了两个参数:
PrepareCell(PaintInfo,NodeBitmap.Width);
进入堆栈变量:
_profiler_WindowLeft := Window.Left; _profiler_NodeBitmapWidth := NodeBitmap.Width; PrepareCell(PaintInfo,_profiler_WindowLeft,_profiler_NodeBitmapWidth);
要确认瓶颈不是,请致电
> Windows.Left,或
> Nodebitmap.Width
Profiler仍然表明该行
PrepareCell(PaintInfo,_profiler_NodeBitmapWidth);
本身就是瓶颈; PrepareCell里面没有任何东西.这必须意味着它是准备单元格的调用设置内部,或者在PrepareCell的开头:
VirtualTrees.pas.15746: PrepareCell(PaintInfo,_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]
问题仍然存在:
>为什么将一个变量推入堆栈,另外两个进入寄存器瓶颈?
>为什么PrepareCell内部没有任何内容成为瓶颈?
解决方法
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