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

 

下一节将是作图游戏的终篇,将研究一下结果验证的策略,顺便美化一下界面!

posted @ 2012-12-17 14:35  地月银光  阅读(1269)  评论(1)    收藏  举报