Silverlight数学引擎(17)——性能优化
曾经有个很有意思的面试题,题目是要求写一个程序,求1到100的累加值。不少人写出来的都是这个样子的:
int sum = 0; for (int i = 1; i <= 100; i++) sum += i;
但是参考答案却是这样的:
int sum = (1 + 100)*50;
这道题没有难度,只是告诉你一个问题:不要把所有的工作都交给电脑,那样你的脑子就要生锈了,从另外一个方面讲,如何才能节省CPU时间提高程序的性能,是我们要常常动脑子去考虑的。
当发现程序存在性能问题时,首先要做的工作当然是找出是什么地方存在不必要的性能损耗,可以有以下几种做法:
1. 先从业务逻辑上分析,找出哪些过程可能存在性能损耗: 比如我们的作图,最终都是通过在画板上更新坐标位置实现的图形的各种依赖变化的,所以这是一个性能检测的方面。
2. 从代码中的计算和算法中找出可以以空间换时间的地方。 比如我们计算交点坐标的一些方法中,存在了很多重复计算的地方。
3. 在程序中添加日志,然后对日志进行分析和数据挖掘。 根据第1点的分析,我们可以在CoordinateBase中的UpdateVisual()方法中记录该方法的调用者和调用次数。
接下来我们就根据上面的步骤来抓取一些性能方面的数据,首先画一些具有依赖的图形,如图所示,我们先画了两个FreePoint A和B,接着画三个中点 E、H、K,用鼠标拖拽点B,我们记录下单次拖拽中各个点调用UpdateVisual()的数据:
AB(LineShape)---1
E(IntersectionPointOfLines)---5
AE(LineShape)---5
H(IntersectionPointOfLines)---25
AH(LineShape)---25
K(IntersectionPointOfLines)---125

可以看出,由于依赖的关系,几乎每个图形的UpdateVisual()的次数都大于1,而且进行中几何级数的增长,例如K是第三个中点,其依赖于第二个中点H,其更新次数是125,可以推算出,如果这样画10个中点的话,只要鼠标移动一个像素,这种更新次数将超过千万,任何CPU都将被秒杀,恐惧了吧?
这种情况稍加分析就可以理解,因为存在重复调用,从下图中可以更明星地看出来:

因为是网状结构,一个子节点会有多个父节点,任何一个父节点的变化都会导致子节点的变化,当这种依赖关系的层次越多,网络图中的路径会随着层次呈几何级数增长。解决这种问题的方法也很简单,就是只选择一条路,不要把每条路都走一遍。当然,这要对我们当前的依赖处理做一些变化,不能采用父类更新子类这种递归的方式了,以下就是找出一条路的方法:
public static void FindAllChilds(this ICoordinate shape, List<ICoordinate> childs) { if (shape.Children != null) { foreach (var child in shape.Children) { if (!childs.Contains(child)) childs.Add(child); child.FindAllChilds(childs); } } }
然后我们需要将这部分逻辑从UpdateVisual()中移到Move(),从而保证更新始终由鼠标触发:
public virtual void UpdateVisual(string updateBy) { if (!BehaviorBase.Setting.ShapeUpdateLog.Keys.Contains(this)) BehaviorBase.Setting.ShapeUpdateLog.Add(this, 0); BehaviorBase.Setting.ShapeUpdateLog[this] ++;//= (updateBy + ","); if (!Center.Exists()) Shape.Visibility = Visibility.Collapsed; else if (visible) Shape.Visibility = visible ? Visibility.Visible : Visibility.Collapsed; //if (Children != null) //{ // foreach (var dep in Children) // { // dep.UpdateVisual(Name); // } //} } public void UpdateAllChilds() { var childs = new List<ICoordinate>(); this.FindAllChilds(childs); foreach (var dep in childs) { dep.UpdateVisual(Name); } }
OK,经过此番优化,再来监测一下UpdateVisual()的调用次数,发现不管添加多少个中点,每个图形的更新次数都只有一次了,哈哈,性能优化也能给自己带来成就感不是吗!
http://www.diyuexi.com/webpages/query/ShareRes.aspx
下一节将是作图游戏的终篇,将研究一下结果验证的策略,顺便美化一下界面!
浙公网安备 33010602011771号