我有一个用VB.NET for Framework 4.5编写的WinForms应用程序。 我注意到应用程序的启动时间非常长(我写的其他应用程序在启动时几乎立即启动更多工作,此应用程序需要> 5秒)启动时间在多次启动后不会改变,所以我猜测在第一次启动应用程序时不是未缓存的CLR代码。
我在启动期间写了一些时间进行了一些测试:
Module modMain
Public MyLog As System.Text.StringBuilder
<STAThread>
Public Sub Main()
MyLog = New System.Text.StringBuilder
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
MyLog.AppendLine("Before run: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
Application.Run(frmMain)
End Sub
End Module
Sub Main()
是应用程序入口点。它运行frmMain
,我控制的第一个真实的东西是Sub InitializeComponent()
,由设计师生成:
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
MyLog.AppendLine("Init Start: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
'All the control initializations
MyLog.AppendLine("Init End: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
End Sub
最后我到达Form.Load
事件
Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MyLog.AppendLine("Form_Load Start: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
'...
MyLog.AppendLine("Form_Load End: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
End Sub
现在,MyLog的输出如下:
Before run: 15.12.2014 19:56:47,579
Init Start: 15.12.2014 19:56:51,451
Init End: 15.12.2014 19:56:51,521
Form_Load Start: 15.12.2014 19:56:51,544
Form_Load End: 15.12.2014 19:56:51,547
您可以看到主要暂停发生在Application.Run()
和Sub InitializeComponent()
之间。我从其他问题中得知,GUI线程已经启动了一个消息循环,但我不知道为什么它应该比这个应用程序慢得多。
所以我的问题是:Application.Run和我重新控制代码之间究竟发生了什么,我能做些什么来加快速度吗?在那里完成的工作是否与表单上的组件有关?
我已尝试使用frmMain.ShowDialog()
代替Application.Run(frmMain)
,但这导致了相同的结果。我使用的是Visual Studio Express,不幸的是我无法使用更深入的性能分析器。
将此标记为C#和VB.NET,因为非常欢迎使用这两种语言的答案。
修改
我做了一些更多的测试,包括在SLaks回答中提出的解决方案。使用NGEN
预编译程序集似乎没有任何明显的效果。所以我想这不是InitializeComponent
代码的JIT编译。
Windows 7: Slow start
Windows 8.1: Fast start
Windows Server 2008: Fast start
这些只是更多线索,我不知道它是否对答案有帮助。
编辑2
在启动期间查看ProcMon,我发现执行挂起在以下几行:
"15:56:29.3547260","Electrochemical Calculator.exe","5972","CreateFile","C:\Users\Jens\Desktop\Electrochemical Calculator Barebone\Electrochemical Calculator\bin\Release\de\Electrochemical Calculator.resources.dll","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:29.3548019","Electrochemical Calculator.exe","5972","CreateFile","C:\Users\Jens\Desktop\Electrochemical Calculator Barebone\Electrochemical Calculator\bin\Release\de\Electrochemical Calculator.resources\Electrochemical Calculator.resources.dll","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:29.3548612","Electrochemical Calculator.exe","5972","CreateFile","C:\Users\Jens\Desktop\Electrochemical Calculator Barebone\Electrochemical Calculator\bin\Release\de\Electrochemical Calculator.resources.exe","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:29.3549519","Electrochemical Calculator.exe","5972","CreateFile","C:\Users\Jens\Desktop\Electrochemical Calculator Barebone\Electrochemical Calculator\bin\Release\de\Electrochemical Calculator.resources\Electrochemical Calculator.resources.exe","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:32.8796760","Electrochemical Calculator.exe","5972","CreateFile","C:\Windows\Fonts\StaticCache.dat","SUCCESS","Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened"
"15:56:32.8797088","Electrochemical Calculator.exe","5972","QueryStandardInformationFile","C:\Windows\Fonts\StaticCache.dat","SUCCESS","AllocationSize: 9,633,792, EndOfFile: 9,633,792, NumberOfLinks: 1, DeletePending: False, Directory: False"
"15:56:32.8797218","Electrochemical Calculator.exe","5972","ReadFile","C:\Windows\Fonts\StaticCache.dat","SUCCESS","Offset: 0, Length: 60, Priority: Normal"
"15:56:32.8797429","Electrochemical Calculator.exe","5972","CreateFileMapping","C:\Windows\Fonts\StaticCache.dat","FILE LOCKED WITH ONLY READERS","SyncType: SyncTypeCreateSection, PageProtection: "
问题仅在发布版本中进一步发生,并且仅在我直接从Windows资源管理器启动程序时才会出现。调试版本立即启动(0.3秒,相比之下5-10秒),从Visual Studio启动时版本构建也是如此。
答案 0 :(得分:12)
好吧,你消除了启动延迟的所有正常来源。它肯定与Application.Run()没有任何关系,它在之后 形式的InitializeComponent()方法完成运行时才开始。并且您通过使用Ngen.exe消除了jitting开销。确保区分冷启动延迟和热启动延迟,如果第一次启动程序时速度很慢,那么这是一个硬件问题,需要更快的磁盘。它在某些机器上运行缓慢的事实强烈地指出了环境问题。
由在InitializeComponent()中运行的代码触发。换句话说,表单上控件的所有构造函数和属性设置器。 通常只需要很少的时间,但肯定有麻烦制造者。你需要寻找一个非平凡的控件,特别是那种使用COM(又名ActiveX)的控件。像WebBrowser一样。还有更多的可能性,类名以&#34; Ax&#34;开头的任何东西。这样的控件可以加载其他DLL的 lot ,并且很容易对安全软件感兴趣。
一些调试提示:
答案 1 :(得分:2)
这段时间用于加载表单控件使用的每个程序集,以及JITting InitializeComponent方法。
答案 2 :(得分:2)
就像汉斯所说,你已经消除了明显的问题,对我来说,问题似乎是环境问题......所以在极端情况下你如何解决这个问题......
由于这个过程一直需要5秒或更长时间(从资源管理器启动时),这里有一些我想在这种情况下采取的方法 -
注意 - 由于存在计时问题,您可能需要重复实验几次以获得不错的结果。显然,我仍然难以保证结果......如果我个人这样做,我希望即使这些高级别的想法不能100%有效,也可以即兴发挥。但我肯定会给他们一个机会。
使用Procdump启动流程,并在进程的n
性能计数器达到某个值后,执行Elapsed Time
完整转储(如2) ,或3或4)。然后你可以在windbg(大多数信息,难以使用)或VS.Net中打开这些转储(更容易使用,但可能会或可能不会显示你想要找到的内容,即使它在那里 - 请使用混合模式调试时打开转储。)
在类似的行上,但功能较少,更难以正确使用..配置ProcessExplorer以在查看流程属性时显示本机堆栈跟踪...一旦启动流程,切换到ProcessExplorer,并查看进程中各种线程的堆栈跟踪。这比较棘手,因为根据有多少线程,手动浏览堆栈跟踪可能无法正常工作..可能值得一个一两枪,如果它确实以最小的努力使问题显而易见。
在任何一种情况下,请确保将符号配置为Microsoft的公共符号服务器。这样您就可以从本机堆栈跟踪中获取大部分信息。
总结一下,这个想法是..在这种情况下,某些高级函数应该具有很高的显示在堆栈跟踪中的概率,如果它下面的东西需要几秒钟才能完成。
注意 - 一旦你知道了高级功能,它可能是故事的结尾,或者可能仍然需要将它与其他系统级进程联系起来。(再次反对 - 病毒是典型的例子)..但你当然可以期待有更多的线索来帮助备份任何这样的假设。