冷启动与热启动的比较
与已经打开过一次(热启动)相比,重新启动(冷启动)后,我们的应用程序启动时间要多得多。
大多数(如果不是全部)差异似乎来自加载DLL,当DLL位于高速缓存的内存页中时,它们的加载速度要快得多。我们尝试使用ClearMem模拟重新引导(因为它比实际重新引导花费的时间少得多),并且得到的结果参差不齐,在某些计算机上,它似乎非常一致地模拟了重新引导,而在某些机器上却没有。
总结一下我的问题是:
- 我们是否经历过冷启动和暖启动之间的启动时间差异?
- 我们如何消除这种差异?
- 我们知道可靠地模拟重启的方法吗?
编辑:
注释的澄清:
- 该应用程序大多数是带有某些.NET的本机C ++(第一个加载的.NET程序集需要CLR付费)。
- 我们正在寻求缩短加载时间,显然,我们完成了分析工作,并改善了代码中的热点。
我忘了提到的是,通过对所有二进制文件进行重新基准化,我们获得了一些改进,因此加载器不必在加载时执行此操作。
解决方案
至于模拟重启,我们是否考虑过从虚拟PC运行应用程序?使用虚拟化,我们可以方便地一遍又一遍地复制一组条件。
我还将考虑使用某种类型的性能分析应用程序来发现导致时间滞后的少量代码,然后做出判断,确定真正需要多少代码,或者是否可以通过其他方式实现。
很难真正模拟软件中的重启。重新启动时,计算机中的所有设备都将置位其重置位,这将导致系统范围内的所有内存丢失。
在现代计算机中,我们到处都有内存和高速缓存:有一个VM子系统为程序存储内存页面,然后我们有OS来将文件内容缓存在内存中,然后才有-硬盘驱动器本身上扇区的磁盘缓冲区。我们可能可以重置OS缓存,但是驱动器上的磁盘缓冲区是?我不知道
例如,使用一种使应用更快启动冷启动(某种程度)的方法。 Adobe Reader,通过在启动时加载一些文件,从而向用户隐藏冷启动。仅在不应该立即启动程序的情况下才可用。
另一个需要注意的是,.NET 3.5SP1可以大大提高冷启动速度,尽管我不能说多少。
我们是如何配置代码的?并非所有的分析方法都是平等的,并且某些发现热点比其他方法更好。我们要加载很多文件吗?如果这样,磁盘碎片和寻找时间可能会起作用。
甚至将基本的时序信息插入代码中,然后写到日志文件中,然后在冷/热启动时检查文件,这将有助于确定应用程序在哪里花费时间。
如果没有更多信息,我将倾向于使用文件系统/磁盘缓存,因为这是两种环境之间的可能差异。如果是这种情况,那么我们或者需要花费更少的时间来预先加载文件,或者需要更快的方式来加载文件。一个示例(可能不适用)是,如果要加载二进制数据文件,则将它们全部组合成一个文件,一次读取将整个文件存储到内存中,然后解析出它们的内容。更少的磁盘存储空间和时间花费在读取磁盘上。同样,也许这并不适用。我不知道有什么工具可以清除磁盘/文件系统缓存,但是我们可以编写一个快速的应用程序来从磁盘上读取一堆无关的文件,以使文件系统/磁盘缓存加载不同的信息。
可能是NIC(LAN卡),并且应用程序依赖于其他某些应用程序
需要网络启动的服务。因此,仅对应用程序进行性能分析可能无法完全告诉我们这一点,但是我们应该检查应用程序的依赖关系。
@Morten Christiansen说:
One way to make apps start cold-start faster (sort of) is used by e.g. Adobe reader, by loading some of the files on startup, thereby hiding the cold start from the users. This is only usable if the program is not supposed to start up immediately.
这使得客户在每次启动时都要为其初始化应用付费,即使它没有使用,我真的不喜欢这种选择(Raymond也不)。
加快应用程序启动的一种成功方法是将DLL切换为延迟加载。这是一个低成本的更改(有些需要项目设置),但可以大大加快启动速度。然后,以性能分析模式运行depends.exe来找出启动过程中仍然加载了哪些DLL,并还原它们上的延迟负载。请记住,我们也可能会延迟加载所需的大多数Windows DLL。
缩短应用程序冷启动时间的一项非常有效的技术是优化功能链接排序。
使用Visual Studio链接器,我们可以在文件中传递要链接的模块中的所有功能的列表(或者仅其中一些功能,不一定是全部功能),并且链接器会将这些功能彼此并排放置在内存中。
当应用程序启动时,通常会在整个应用程序中调用init函数。这些调用中的许多都将访问尚不在内存中的页面,从而导致页面错误和磁盘查找。那就是启动缓慢的原因。
优化应用程序以使所有这些功能结合在一起将是一个巨大的胜利。
在Visual Studio 2005或者更高版本中查看Profile Guided Optimization。 PGO为我们做的一件事就是功能链接排序。
进入构建过程有点困难,因为使用PGO,我们需要链接,运行应用程序,然后与运行时配置文件的输出重新链接。这意味着构建过程需要有一个运行时环境,并在不良构建等之后进行清理,但是通常在不更改代码的情况下,冷启动的收益通常会提高10倍或者更多。
这里有关于PGO的更多信息:
http://msdn.microsoft.com/zh-CN/library/e7k32f4k.aspx
作为功能顺序列表的替代方法,只需将将在相同部分中调用的代码分组:
#pragma code_seg(".startUp") //... #pragma code_seg #pragma data_seg(".startUp") //... #pragma data_seg
随着代码的更改,它应该易于维护,但具有与功能顺序列表相同的好处。
我不确定函数顺序列表是否也可以指定全局变量,但是使用此#pragma data_seg可以简单地工作。
如果应用程序不是很复杂,则可以将所有可执行文件复制到另一个目录,这类似于重新启动。 ("剪切和粘贴"似乎无法正常工作,Windows足够智能,可以知道将文件移动到另一个文件夹中并缓存在内存中)