对可执行的应用程序,它的生命是从Load开始的,一个.NET 的程序,某种程度上可以说它的生命是从加载类型开始的。本文阐述了在.NET CF中的Type Loader的工作原理,并结合示例说明了如何让您的应用程序启动更快。
Keywords
.NET Compact Framework,Type Loader, JIT ,Generic,Dictionary
相关文章
第零回.序和属性
第一回.真的了解.NET CF吗?
第二回.初窥CF类型加载器
摘要
对可执行的应用程序,它的生命是从Load开始的,一个.NET 的程序,某种程度上可以说它的生命是从加载类型开始的。本文阐述了在.NET CF中的Type Loader的工作原理,并结合示例说明了如何让您的应用程序启动更快。
Keywords
.NET Compact Framework,Type Loader, JIT ,Generic,Dictionary
1. 设备不能承受之慢
等待是很痛苦的,让用户等待是不人道的。现在PC机上的程序也许感觉不是很明显,因为桌面计算机性能普遍较佳,而且一般的应用程序不会涉及海量数据的运算,日常的程序即使有性能上的某些缺陷,用户也不会明显的察觉到。
然而在CPU处理能力和内存均有限的移动设备上,计算机的工作能力就没有那么可观了。也许一个简单的程序就能让你的设备陷入肉眼就能识别的性能危机。设想一下用户怀着愉悦的心情在你的程序中选择了一个菜单项,但是他那台不怎么样的设备却需要反应数十秒,用户也只能望着屏幕中央不断旋转的光标兴叹了,这无疑对用户来说是一个打击,软件开发人员更是颜面无光。
好吧,下面我们就来看看下面一个简单的程序是如何折腾你的CLR的,虽然我刻意将它极端化了一点点J
public partial class Form1 : Form
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
public Form1()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
InitializeComponent();
![](/Images/OutliningIndicators/InBlock.gif)
int[] r = new int[6];
int n = 0;
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
安置一些计时的环节,并调用funcX()触发Type Loader#region 安置一些计时的环节,并调用funcX()触发Type Loader
r[n++] = func0(Environment.TickCount);
r[n++] = func1(Environment.TickCount);
r[n++] = func2(Environment.TickCount);
r[n++] = func3(Environment.TickCount);
r[n++] = func4(Environment.TickCount);
r[n++] = func5(Environment.TickCount);
#endregion
![](/Images/OutliningIndicators/InBlock.gif)
//将结果保存到本地文本文件中
using (StreamWriter writer = new StreamWriter(@"\Temp\LoaderPerf.txt", false))
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
for (int i = 0; i < n; ++i) writer.WriteLine(r[i]);
}
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 返回一个整型值,单位为毫秒
/// 指示从Type Load开始到方法开始执行的时间差
/// </summary>
/// <returns>初始化类型所耗费的时间(毫秒)</returns>
public static int func0(int start)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int diff = Environment.TickCount - start;
Maps0.func();
return diff;
}
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ContractedSubBlock.gif)
与func0一致的其他五个方法#region 与func0一致的其他五个方法
![](/Images/OutliningIndicators/InBlock.gif)
public static int func1(int start)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int diff = Environment.TickCount - start;
Maps1.func();
return diff;
}
![](/Images/OutliningIndicators/InBlock.gif)
public static int func2(int start)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int diff = Environment.TickCount - start;
Maps2.func();
return diff;
}
![](/Images/OutliningIndicators/InBlock.gif)
public static int func3(int start)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int diff = Environment.TickCount - start;
Maps3.func();
return diff;
}
![](/Images/OutliningIndicators/InBlock.gif)
public static int func4(int start)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int diff = Environment.TickCount - start;
Maps4.func();
return diff;
}
![](/Images/OutliningIndicators/InBlock.gif)
public static int func5(int start)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int diff = Environment.TickCount - start;
Maps5.func();
return diff;
}
![](/Images/OutliningIndicators/InBlock.gif)
#endregion
}
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
/// 定义了一个有些夸张的类
/// 它定义了5个枚举5个泛型字典并做了初始化
/// </summary>
public static class Maps0
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum1
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum2
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum3
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum4
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum5
{ i1, }
private static readonly Dictionary<Enum1, int> map1 = new Dictionary<Enum1, int>();
private static readonly Dictionary<Enum2, int> map2 = new Dictionary<Enum2, int>();
private static readonly Dictionary<Enum3, int> map3 = new Dictionary<Enum3, int>();
private static readonly Dictionary<Enum4, int> map4 = new Dictionary<Enum4, int>();
private static readonly Dictionary<Enum5, int> map5 = new Dictionary<Enum5, int>();
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void func()
{ }
}
![](/Images/OutliningIndicators/None.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
与Maps0一样的其他五个类#region 与Maps0一样的其他五个类
![](/Images/OutliningIndicators/InBlock.gif)
public static class Maps1
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum1
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum2
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum3
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum4
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum5
{ i1, }
private static readonly Dictionary<Enum1, int> map1 = new Dictionary<Enum1, int>();
private static readonly Dictionary<Enum2, int> map2 = new Dictionary<Enum2, int>();
private static readonly Dictionary<Enum3, int> map3 = new Dictionary<Enum3, int>();
private static readonly Dictionary<Enum4, int> map4 = new Dictionary<Enum4, int>();
private static readonly Dictionary<Enum5, int> map5 = new Dictionary<Enum5, int>();
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void func()
{ }
}
![](/Images/OutliningIndicators/InBlock.gif)
public static class Maps2
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum1
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum2
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum3
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum4
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum5
{ i1, }
private static readonly Dictionary<Enum1, int> map1 = new Dictionary<Enum1, int>();
private static readonly Dictionary<Enum2, int> map2 = new Dictionary<Enum2, int>();
private static readonly Dictionary<Enum3, int> map3 = new Dictionary<Enum3, int>();
private static readonly Dictionary<Enum4, int> map4 = new Dictionary<Enum4, int>();
private static readonly Dictionary<Enum5, int> map5 = new Dictionary<Enum5, int>();
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void func()
{ }
}
![](/Images/OutliningIndicators/InBlock.gif)
public static class Maps3
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum1
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum2
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum3
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum4
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum5
{ i1, }
private static readonly Dictionary<Enum1, int> map1 = new Dictionary<Enum1, int>();
private static readonly Dictionary<Enum2, int> map2 = new Dictionary<Enum2, int>();
private static readonly Dictionary<Enum3, int> map3 = new Dictionary<Enum3, int>();
private static readonly Dictionary<Enum4, int> map4 = new Dictionary<Enum4, int>();
private static readonly Dictionary<Enum5, int> map5 = new Dictionary<Enum5, int>();
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void func()
{ }
}
![](/Images/OutliningIndicators/InBlock.gif)
public static class Maps4
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum1
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum2
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum3
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum4
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum5
{ i1, }
private static readonly Dictionary<Enum1, int> map1 = new Dictionary<Enum1, int>();
private static readonly Dictionary<Enum2, int> map2 = new Dictionary<Enum2, int>();
private static readonly Dictionary<Enum3, int> map3 = new Dictionary<Enum3, int>();
private static readonly Dictionary<Enum4, int> map4 = new Dictionary<Enum4, int>();
private static readonly Dictionary<Enum5, int> map5 = new Dictionary<Enum5, int>();
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void func()
{ }
}
![](/Images/OutliningIndicators/InBlock.gif)
public static class Maps5
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum1
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum2
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum3
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum4
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Enum5
{ i1, }
private static readonly Dictionary<Enum1, int> map1 = new Dictionary<Enum1, int>();
private static readonly Dictionary<Enum2, int> map2 = new Dictionary<Enum2, int>();
private static readonly Dictionary<Enum3, int> map3 = new Dictionary<Enum3, int>();
private static readonly Dictionary<Enum4, int> map4 = new Dictionary<Enum4, int>();
private static readonly Dictionary<Enum5, int> map5 = new Dictionary<Enum5, int>();
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public static void func()
{ }
}
![](/Images/OutliningIndicators/InBlock.gif)
#endregion
![](/Images/OutliningIndicators/None.gif)
以上代码在Windows Mobile 6 Professional的模拟器上运行时,得到的txt文件如下:![](/images/cnblogs_com/fox23/wm6proResult.jpg)
可以看到反应(加载类型的时间)基本在1秒左右,而且逐渐增长,原因下文中会做详细的解释,当然,这些数据和机器性能也是有关的。但是差别不会太大。
同样的程序在我的Samsung i718上测试结果为如下:
406
377
553
993
1678
2126.
虽然跟模拟器相比开始时效果要好一点,但还是一个数量级的
如果在你的应用程序加载几个类型就要花费CLR以秒为单位的时间,那用户估计要抓狂了。
2. 到底发生了什么?(What happened when JITing?)
前面只是举了一个略有夸张的例子,也许你还不明白它夸张在哪里,但它确实是有可能存在的,比如说我们的查询服务,比如通信数据中要对某些对象进行序列化处理,比如多机通信系统有时可能要用到的很多的Hashtable,Dictionary等等。下面我们就来看看,刚才那段代码在执行的时候,CF CLR发生了什么。
在代码执行到Maps0.func()的时候,对方法func()的调用会触发CF CLR的Type Loader的工作(这是JIT的工作方式,我也曾在这里提到)。为什么会在这里触发Type Loader呢?
因为之前的代码并没有访问到Maps0咯,如果Type Loader检测到该类型已经被构造出来,那么它将立即return,以免做重复的工作。
加载Maps0又会立即导致这个包含Maps0的模块(module)被标记为忙碌(busy)。
注意,这里说的是module而不是说这个App.exe,尽管此时的app的确是busying。
在加载Maps0的过程中,CF Loader会遇到Maps0所包含的某些静态类型字段如Dictionary< Maps0.Enum1, int>,这些类型同样是他从来没遇到的,于是,在JIT的时候这样的Load会递归的进行下去,直到涉及的类型都被Load完毕。
好,说到加载Dictionary< Maps0.Enum1, int>,这时,由于mscorlib.dll包含有Dictionary<T, U>的定义,现在mscorelib.dll同样会被打上一个忙碌中(busy)的标记。
接着,CF Loader会加载DictionaryEntry<Maps0.Enum1,int>,因为在Dictionary的内部会有一些数组结构,用来存储键值对,而每一个这样的键值对是一个DictionaryEntry,关于DictionaryEntry你可以在MSDN找到更详尽的解释。
而在加载DictionaryEntry<Maps0.Enum1,int>的时候,CLR又发现了类型Maps0.Enum1(一开始在Dictionary的时候就遇到了Maps0.Enum1),这时Loader将再次尝试去加载Maps0.Enum1。然而当Loader抛开mscorlib.dll(因为DictionaryEntry并不在mscorlib.dll中)而转向程序集app.exe它会发现,app.exe中那个包含Maps0的模块被标记为busy,所以它并不能访问,这其实是一个博弈的问题。所以上面的代码初看似乎除了冗余似乎没什么大问题,但实际上并不是一种好的实现方式。
而此时访问的Maps0.Enum1将被描述为处于一个“部分加载”(待结束) 的状态。
同样,包含对Maps0.Enum1引用的DictionaryEntry<Maps0.Enum1, int> 和Dictionary<Maps0.Enum1, int> 也会处于这样的部分加载的状态。由于Loader从对这些类型的加载中不成功地返回,最后它会扫描当前的module中的每一种类型,不管他们是返回的是完整加载还是“部分加载”状态。不过个人认为这里面有一定的可以优化的空间,虽然这样的扫描是必要的,因为不能保证返回的加载成功的类型运行时不会访问到“部分加载”的类型。
所有的加载到的类型扫描完毕后,Loader此时会尝试去完成扫描到的所有“部分加载”的类型。最终Loader 还是会回到起初对Maps0的调用的地方,并发现Maps0.Enum1处于“部分加载”的状态,这个时候才将Maps0的Enum1完整加载。
接下来,Loader会按照相同的方式去加载Maps0的其他成员。被Enum1“阻碍”的其他类型也都将被顺利加载,因为此时的Loader被告知Maps0.Enum类型已经加载过了。上面提到的这个问题就像一条拉链,如果每一个环节都拉紧了,最终还是只能从头把它拉开。
可是还没完呢,别放松得太早!这才一个Enum1呢,接着执行下去Loader会尝试加载Dictionary<Maps0.Enum2, int>,而这时候同样的问题会再次发生,因为在加载DictionaryEntry<Maps0.Enum2, int>的时候,Maps0.Enmu2之前已处于“部分加载状态”,然后以上的一切会重演。如此循环下去,到足够的次数Maps0类型终于被Load完毕了!汗,确实是非常纠结和缓慢的过程,每一次,当Type Loader尝试在App.exe中加载EnumX并以失败返回时,它都必须循环访问在mscorelib.dll和App.exe中每一个已加载的类型 (包括未加载完全的类型)。
现在你也许会感叹了,上面那段看似和谐的代码给你带来了多么痛苦的一次CLR之旅!Got twisted?!头晕了吗?
另外,我们可以从输出的结果中看到,从Maps0到MapsN,所耗费的时间是越来越长的,这又是什么原因呢?其实仔细分析我们可以知道,由于加载的类型逐渐增多,当每一次遍历他们,并获取其加载状况的时候,所需要的代价(资源,时间)也会越来越多,这个是很好理解的。
最后,再来回顾一下上面描述的问题,可以发现,主要的耗费在于那个隐藏的博弈的怪圈,简单来说,事情就是这样的: 类型 A在模块A.dll中,但它引用了一个类型B,但是这个类型B又引用了另外一个类型A2,有趣的是这个A2却正好位于已谢绝访问的A模块中,而且A2并没有被完全加载。问题就在这里,所以必须让A2先加载完毕,但是这之前Loader却要扫描所有已加载的类型,现在应该很清楚了吧。头还晕的同志可以喝杯茶再多看两遍。呵呵。虽然这里的泛型字典有点特殊,但是它很好的反映了问题,接下来,来看看改如何优化这个程序。
3. 较好的解决方案
针对这个问题,我们该如何提高程序的性能呢?
这里有两种方式:
1)我们可以通过转移的方式打破这种循环依赖的结构。把Maps类型或者Enum类型放到另外的程序集中去。
2)我们可以通过某些手段让这所有的Enum类型都先加载,后面的Maps的内容涉及EnumX的就不再需要重复加载了。
下面来来看看这两种方案具体是如何实现的
第一种方案是从编程原则上面说的,这很好理解,我们在编写程序的时候应当使所有涉及的引用是“前向”的,也就是说,尽量避免这种往“回”的引用出现,以免造成环形引用。这有点像那个Philosopher的例子,只不过那个例子会造成Deadlock,而这里的lock最终会由CLR费力的解开。而这都是应该尽量避免的。
第二种方案也比较好理解,但是实现起来可能会比较繁琐,
也许你会设想,要是在调用Maps0.fun()之前先new一个Enum1的实例不就好了吗?但这是不可能实现的,成员类的调用还是会首先导致父类的加载。而这还是回到了之前出现的那个问题上了。
我们需要作的应该是把EnmuX类型均移到Maps0类型之外,并放到另一个类型中去,比如class EnumsInMaps0,或者就让他们在最外层的class内也行。然后去让这个EnumsInmaps中的Enums先实例化,如下:
//我把Maps0,Maps1,Maps2中的Enum那出来,放到EnumInMaps中:
class EnumInMaps
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps0Enum1
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps0Enum2
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps0Enum3
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps0Enum4
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps0Enum5
{ i1, }
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps1Enum1
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps1Enum2
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps1Enum3
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps1Enum4
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps1Enum5
{ i1, }
![](/Images/OutliningIndicators/InBlock.gif)
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps2Enum1
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps2Enum2
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps2Enum3
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps2Enum4
{ i1, }
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public enum Maps2Enum5
{ i1, }
![](/Images/OutliningIndicators/InBlock.gif)
public void LoadMessage()
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// MessageBox.Show("Enums Loaded!");
}
}
![](/Images/OutliningIndicators/None.gif)
注意,这里也许单纯的new还不行,即使是你为这个EnumsInMaps创建了一个实例,仍然不能保证它的每个成员都被JIT了。我们姑且踏实一点,这里我不妨这样:
public static void PreLoadEnums()
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
EnumInMaps.Maps0Enum1.i1.ToString();
EnumInMaps.Maps0Enum2.i1.ToString();
EnumInMaps.Maps0Enum3.i1.ToString();
EnumInMaps.Maps0Enum4.i1.ToString();
EnumInMaps.Maps0Enum5.i1.ToString();
![](/Images/OutliningIndicators/InBlock.gif)
EnumInMaps.Maps1Enum1.i1.ToString();
EnumInMaps.Maps1Enum2.i1.ToString();
EnumInMaps.Maps1Enum3.i1.ToString();
EnumInMaps.Maps1Enum4.i1.ToString();
EnumInMaps.Maps1Enum5.i1.ToString();
![](/Images/OutliningIndicators/InBlock.gif)
EnumInMaps.Maps2Enum1.i1.ToString();
EnumInMaps.Maps2Enum2.i1.ToString();
EnumInMaps.Maps2Enum3.i1.ToString();
EnumInMaps.Maps2Enum4.i1.ToString();
EnumInMaps.Maps2Enum5.i1.ToString();
}
![](/Images/OutliningIndicators/None.gif)
直接在Enums上调用方法,这总可以了吧,好,现在我们就可以让enumX顺利加载了,也可以随意实例化了,不用担心什么循环依赖。你也不必总是保留一个实例,类型一旦被加载,在这个应用程序的生命周期内它都会被标记为已加载,而且生成的本地代码也会被缓存(如果没有内存问题的话)。所以即便没有了实例,这个类依然存在着。
现在,我们需要把对这些enumX的处理交给一个函数去做。当然,这个函数应当是较早调用的,至少要比加载Maps或者他的成员而调用它们的时候更早些。
注意:为了便于在同一份代码中对比,这里只对Maps0,Maps1,Maps2进行了修改,后面三个类依旧保持原样
执行结果如下:
![](/images/cnblogs_com/fox23/optedLoad.jpg)
在我的Samsung i718上面测试结果如下:
4
4
42
845
1358
1797
可以看到,经过修改的Maps0,Maps1,Maps2的Load时间加起来不过几十毫秒,较之前面的代码,速度提高了百倍左右,看来我们修改的效果是明显的。
4. 类型加载的几句你必须知道的废话
再多说几句关于类型构造器的话吧。
首先,编写类型时,要时刻想到CLR在加载它的时候会有哪些行为,你的代码逻辑是否会导致交叉引用而对CLR造成前面提到的困惑。
另外,构造时要注意CLR的“自动化”行为。要弄清需要的是静态构造器还是实例构造器,默认的CLR是否会调用无参构造器,是否每个构造器都导致了其他成员的初始化等等。这里面仍然有着可优化空间。
最后,关于前两天有网友问到的,Windows Mobile上的程序是否有必要检查其版本唯一性?也就是说,是否需要自己在程序中保证当前运行的只有一份自己的程序的实例。
这个问题要分情况考虑:
如果你的程序是纯本地代码编写(without CLR),那么跟在WinCE下无异,你需要在你的程序检查当前的运行的程序(当然,方法很多,比如CreateEvent捕获异常等等,这里不做详细介绍)。
如果你的程序是托管的(with CLR Supports),那么你设备上的.NET Compact Framework CLR会帮你做这个维护,保证你的应用程序不会出现多份。CLR此时的工作如下模式:
首先,CLR会找到你的程序入口点,在尝试加载你的应用程序之前它会检查程序集的信息,看要加载的应用程序是否在当前已请求Singleton的程序清单上,如果没有则证明是首次执行程序,然后再加载该应用程序到CLR中,然后请求Singleton保护。
简单说来如下所示:
AppStart();
CheckSingletonMutex();
LoadAppIntoCLR();
AcquireSingletonMutex();
但是,当你启动的间隔极短,在Check Singleton还没完成的时候,还是有可能出现多个你的应用程序实例同时存在的情况。所以说,作为正式的产品,这样的检查还是有必要的。
点此处下载代码示例
总结
程序的性能总是在我们不经意间浪费掉了。PC机的开发也许感觉还不是太明显,作为移动设备,性能问题却十分要命。
类型的加载是JIT的,托管应用程序是CLR掌管的一个运行实例,JIT是CLR的必杀技,CLR是.NET的灵魂。作为移动设备的程序员,在享受CF CLR带来的种种便利的同时,也应该为CLR想想,尽量减轻它额外的负担,让你的应用程序享受裸奔一样的快感!
Regards
Reference:
MSDN
Jeffrey Richter CLR via C# Second Edition
.NET Compact Framework 社区
---
©Freesc Huang
黄季冬<fox23>@HUST
2008/3/1