zoukankan      html  css  js  c++  java
  • 转 C# 性能优化之斤斤计较篇

    静态方法比实例方法快吗?

    我们总是从各个渠道听说:静态方法比实例方法要快,所以,我想亲自试试。测试方法很简单,循环调用实例方法和静态方法。

    /// <summary>
    /// 这是一个普通类,调用实例的方法
    /// </summary>
    public class C1 {
        public void DoLoop() {
            for (int i = 0; i < int.MaxValue; i++) {
                DoIt();
            }
        }
     
        private void DoIt() {
        }
    }
     
    /// <summary>
    /// 使用静态方法调用。
    /// </summary>
    public static class C2 {
        public static void DoLoop() {
            for (int i = 0; i < int.MaxValue; i++) {
                DoIt();
            }
        }
     
        private static void DoIt() {
        }
    }

    测试结果如下:

    测试多次,基本偏差不大,只能说,静态方法比实例方法快那么可怜的一点点,鉴于实例方法的灵活性远大于静态方法,所以还是一般使用实例方法吧

    也实验过,在方法中访问实例字段和静态字段,发现也没有区别,所以不再单独罗列代码。

    避免方法内创建实例的情况

    这个要讨论的问题有点难说明,我们还是先看一看.net内部的代码吧,下面是一段Collection<T>.Add的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public void Add(T item)
    {
        if (this.items.IsReadOnly)
        {
            ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
        }
        int count = this.items.Count;
        this.InsertItem(count, item);
    }

    注意ThrowHelper类,如果换成我们自己写,一句话就搞定了:throw new NotSupportedException。为什么微软要这么写呢?

    老外有解释:Why does SortedList implementation use ThrowHelper instead of throwing directly?

    其实,我也是信奉此真理,而且就在前一段时间,一位同事还找我问,两段几乎一样的代码,为什么测试性能有差距,结果我按照此原理,将异常抛出放在外面,结果真的变好了。

    现在,我还要再次测试一下,我相信的是数据:

    class C1 {
        private Dictionary<int, int> _dict = new Dictionary<int, int>() ;
        public C1() {
            _dict.Add(1, 1);
            _dict.Add(2, 2);
        }
     
        public void Do1() {
            object obj = new object();
            for (int i = 0; i < int.MaxValue/100; i++) {
                GetItOne(1);
            }
        }
     
        //这个方法,在内部可能创建实例
        private int GetItOne(int key) {
            int value;
            if (!_dict.TryGetValue(key,out value)) {
                throw new ArgumentOutOfRangeException("key");
            }
            return value;
        }
     
        public void Do2() {
            for (int i = 0; i < int.MaxValue/100; i++) {
                GetItTwo(1);
            }
        }
     
        //这个方法,将创建实例的代码移动到外部
        private int GetItTwo(int key) {
            int value;
            if (!_dict.TryGetValue(key, out value)) {
                ThrowArgumentOutOfRangeException();
            }
            return value;
        }
     
        private static void ThrowArgumentOutOfRangeException() {
            throw new ArgumentOutOfRangeException("key");
        }
    }

    测试结果是:

    基本上,会快0.06秒左右,但是如此大的循环得到的好处并不是那么的明显,但有作用。这种写法还是比较舒服的,所以还是建议大家用吧

    C# 性能优化之斤斤计较篇  二

    上篇继续,本文的完整源代码也在上篇文章中。

    枚举数组和普通枚举性能差异

    有些人可能知道,.net在处理枚举时,对于数组有特别的优化,所以,当枚举的集合是一个数组时,性能会好些。例如下面的测试代码:

    复制代码
     1     class C1 {
     2 
     3         public void Do1() {
     4             int[] array = { 1, 2, 3, 4 };
     5             for (int i = 0; i < int.MaxValue/100; i++) {
     6                 DoIt1(array);
     7             }
     8         }
     9 
    10         private void DoIt1<T>(IEnumerable<T> array) {
    11             foreach (var item in array) {
    12 
    13             }
    14         }
    15 
    16         public void Do2() {
    17             int[] array = { 1, 2, 3, 4 };
    18             for (int i = 0; i < int.MaxValue/100; i++) {
    19                 DoIt2(array);
    20             }
    21         }
    22 
    23         private void DoIt2(int[] array) {
    24             foreach (var item in array) {
    25 
    26             }
    27         }
    28     }
    复制代码

    第23行的方法中,编译器提前已知是一个数组的枚举,所以会优化指令。那么,到底这种优化差距有多大呢?我需要试验一下。

    第一个是Do1的结果,第二个是Do2的结果,显而易见,差距还是相当大的,为什么呢?从反编译的IL代码来看,第一个方法使用标准的GetEnumerator机制,需要创建实例,而且要调用Current和MoveNext两个方法,更何况,Array的GetValue实现实在不够快。而第二个方法使用了for的机制,无需创建实例,不断累加和一个判断语句即可,性能当然高了。

    在Linq to Object中,其实是有这样考虑的代码的。例如:

    复制代码
    public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        if (selector == null)
        {
            throw Error.ArgumentNull("selector");
        }
        if (source is Enumerable.Iterator<TSource>)
        {
            return ((Enumerable.Iterator<TSource>)source).Select<TResult>(selector);
        }
        if (source is TSource[])
        {
            return new Enumerable.WhereSelectArrayIterator<TSource, TResult>((TSource[])source, null, selector);
        }
        if (source is List<TSource>)
        {
            return new Enumerable.WhereSelectListIterator<TSource, TResult>((List<TSource>)source, null, selector);
        }
        return new Enumerable.WhereSelectEnumerableIterator<TSource, TResult>(source, null, selector);
    }
    复制代码

    创建类和结构的性能差异以及属性和字段的性能差异

    下面我还要试验创建类实例和结构类型,其性能差异到底有多大?会不会.net的垃圾回收超级厉害,基本上差异不大呢?当然,我也顺手测试了访问属性和访问字段的差别。

     
    复制代码
     1     class C1 {
     2         public void Do1() {
     3             for (int i = 0; i < int.MaxValue/10; i++) {
     4                 var p = new PointClass() { X = 1, Y = 2 };
     5             }
     6         }
     7         public void Do2() {
     8             for (int i = 0; i < int.MaxValue/10 ; i++) {
     9                 var p = new PointClass2() { X = 1, Y = 2 };
    10             }
    11         }
    12         public void Do3() {
    13             for (int i = 0; i < int.MaxValue/10; i++) {
    14                 var p = new Point() { X = 1, Y = 2 };
    15             }
    16         }
    17         public void Do4() {
    18             for (int i = 0; i < int.MaxValue / 10; i++) {
    19                 var p = new Point() { XP = 1, YP = 2 };
    20             }
    21         }
    22     }
    23 
    24 
    25     class PointClass {
    26         public int X { get; set; }
    27         public int Y { get; set; }
    28     }
    29 
    30     class PointClass2 {
    31         public int X;
    32         public int Y;
    33     }
    34 
    35     struct Point {
    36         public int X;
    37         public int Y;
    38 
    39         public int XP { get { return X; } set { X = value; } }
    40         public int YP { get { return Y; } set { Y = value; } }
    41     }
  • 相关阅读:
    从程序员到项目经理(5):程序员加油站 不是人人都懂的学习要点
    从程序员到项目经理(14):项目经理必须懂一点“章法”
    从程序员到项目经理(17):你不是一个人在战斗思维一换天地宽
    从程序员到项目经理(19):想改变任何人都是徒劳的
    tp5时间戳转换日期格式
    win10让人愤怒的磁盘占用100%问题
    uniapp将时间日期格式化的组件unidateformat的用法
    Asp.Net大型项目实践系列第二季(一)哥欲善其事,必先利其器...
    Asp.Net大型项目实践(9)ExtJs实现系统框架页(非iframe,附源码,在线demo)
    Asp.Net大型项目实践(10)基于MVC Action粒度的权限管理(在线demo,全部源码)
  • 原文地址:https://www.cnblogs.com/baiyu/p/2815994.html
Copyright © 2011-2022 走看看