相对于 x86,x64 下 Winforms 程序的启动速度慢 10 倍
Startup of Winforms program 10x slower under x64 relative to x86
我用 C# 创建了一个流行的 Winforms 程序,它有很多 GUI 小部件,发现当目标平台是 x64 时,启动速度比 x86 慢 5-10 倍左右。在 x64 目标下,启动大约需要 5 秒,这会对用户体验产生负面影响。我想让它快点。
我也试过我的另一个程序,也发现x64下的启动时间是x86的两倍或三倍。
所以我开始想知道是什么原因造成的。我的程序使用了很多小部件,所以为了测试理论,我决定创建一个包含 800 个按钮的测试项目!我将它们全部设置为 Visible=False
,这样 redrawing/refresh 速度就不会搅浑水了。
令我惊讶的是,x64 的启动速度比 x86 的同等速度慢很多。然后我继续为 InitializeComponent();
部分计时,果然,x64 版本 运行 大约用了 3.5 秒。另一方面,x86 只用了大约 0.275 秒。这几乎快了 13 倍!
.NET Framework 2.0、3.0 和 3.5 同样糟糕。在 x64 下以 .NET 4 和 4.5 为目标要好得多,大约为 0.8 秒,但这仍然慢了大约 3 倍(x86 大约为 0.28 秒),我想使用 .NET 3.5 来增加用户群。
所以我的问题是:是什么导致 x64 版本启动如此缓慢,我怎样才能让它更快以便与 x86 版本相媲美?
如果有人想立即测试,我已经创建了一个 VS 2010 项目的 zip,可以在这里下载:http://www.skytopia.com/stuff/64_vs_32bit_Startup_Speed.zip
这是 10000 行 InitializeComponent
函数的 JIT 成本。
如果您测量调用 InitializeComponent
和执行其第一行之间的时间,那是大部分成本。 (只需在 InitializeComponent
的顶部插入一行以进行测量。)
如果你使用VS性能分析器,它会显示大部分时间花费在ThePreStub
,这与JIT有关。
64 位 JITter 比 32 位 JITter 编译代码花费的时间更长,但作为交换,它会生成更好的代码。
Microsoft 正在开发新版本的 JITter,称为 RyuJIT。它源自 32 位 JITter,具有相似的特性(快速编译输出更差的代码)。它将成为.NET 未来版本中的标准JIT。
.NET 4.5 在我的机器上将成本从 2.0 秒减少到 1.3。这可能是由于 4.0 运行时中的 JIT 改进。
等效循环比您的 InitializeComponent
函数快 很多。
如果您想在设计器中创建所有组件,这对您没有帮助,但如果您混合了要在设计器中编辑的重复控件和组件,则可以使用循环。把它放在 Form1.cs
而不是 Form1.designer.cs
这样它就不会被设计者覆盖。
在程序集上使用 NGen 应该可以消除 JIT 成本。但它的缺点是必须与 GAC 打交道。
我用 C# 创建了一个流行的 Winforms 程序,它有很多 GUI 小部件,发现当目标平台是 x64 时,启动速度比 x86 慢 5-10 倍左右。在 x64 目标下,启动大约需要 5 秒,这会对用户体验产生负面影响。我想让它快点。
我也试过我的另一个程序,也发现x64下的启动时间是x86的两倍或三倍。
所以我开始想知道是什么原因造成的。我的程序使用了很多小部件,所以为了测试理论,我决定创建一个包含 800 个按钮的测试项目!我将它们全部设置为 Visible=False
,这样 redrawing/refresh 速度就不会搅浑水了。
令我惊讶的是,x64 的启动速度比 x86 的同等速度慢很多。然后我继续为 InitializeComponent();
部分计时,果然,x64 版本 运行 大约用了 3.5 秒。另一方面,x86 只用了大约 0.275 秒。这几乎快了 13 倍!
.NET Framework 2.0、3.0 和 3.5 同样糟糕。在 x64 下以 .NET 4 和 4.5 为目标要好得多,大约为 0.8 秒,但这仍然慢了大约 3 倍(x86 大约为 0.28 秒),我想使用 .NET 3.5 来增加用户群。
所以我的问题是:是什么导致 x64 版本启动如此缓慢,我怎样才能让它更快以便与 x86 版本相媲美?
如果有人想立即测试,我已经创建了一个 VS 2010 项目的 zip,可以在这里下载:http://www.skytopia.com/stuff/64_vs_32bit_Startup_Speed.zip
这是 10000 行 InitializeComponent
函数的 JIT 成本。
如果您测量调用
InitializeComponent
和执行其第一行之间的时间,那是大部分成本。 (只需在InitializeComponent
的顶部插入一行以进行测量。)如果你使用VS性能分析器,它会显示大部分时间花费在
ThePreStub
,这与JIT有关。64 位 JITter 比 32 位 JITter 编译代码花费的时间更长,但作为交换,它会生成更好的代码。
Microsoft 正在开发新版本的 JITter,称为 RyuJIT。它源自 32 位 JITter,具有相似的特性(快速编译输出更差的代码)。它将成为.NET 未来版本中的标准JIT。
.NET 4.5 在我的机器上将成本从 2.0 秒减少到 1.3。这可能是由于 4.0 运行时中的 JIT 改进。
等效循环比您的
InitializeComponent
函数快 很多。如果您想在设计器中创建所有组件,这对您没有帮助,但如果您混合了要在设计器中编辑的重复控件和组件,则可以使用循环。把它放在
Form1.cs
而不是Form1.designer.cs
这样它就不会被设计者覆盖。在程序集上使用 NGen 应该可以消除 JIT 成本。但它的缺点是必须与 GAC 打交道。