c# - 反射测试未显示预期数字

标签 c# performance reflection c#-4.0 delegates

我已经编写了一些测试代码来比较使用直接属性访问或反射或使用委托(delegate)进行反射的性能。但我得到的结果令人费解,因为它表明反射并不比直接访问属性慢很多(~4%),我不认为这是真的。如果我在这里做错了什么,有人可以告诉我吗?


对于 5000 个项目,我得到以下结果

  • 直接访问:32.2609 秒
  • 反射(reflection):33.623 秒反射(reflection)
  • 使用委托(delegate):31.7981 秒

代码:

private static Random random = new Random((int)DateTime.Now.Ticks);
Private Dictionary<string, Delegate> delegateList = new Dictionary<string, Delegate>(); 
private List<ReflectClass1> dataList = new List<ReflectClass1>();

    private void TestMethod2<T>()
    {
        foreach (var propertyInfo in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (propertyInfo.PropertyType.BaseType != typeof(ValueType))
            {
                Func<T, object> getPropDelegate =
                    (Func<T, object>) Delegate.CreateDelegate(typeof (Func<T, object>), null, propertyInfo.GetGetMethod());
                delegateList.Add(propertyInfo.Name, getPropDelegate);
            }
            //else
            //{
            //    Type propertyType = propertyInfo.PropertyType.GetType();
            //    delegateList.Add(propertyInfo.Name,
            //                     Delegate.CreateDelegate(typeof(Func<T, TResult>), null, propertyInfo.GetGetMethod()));
            //}
        }
    }
    //http:_//stackoverflow.com/questions/1122483/c-random-string-generator     
    private string RandomString(int size)
    {
        StringBuilder builder = new StringBuilder();
        char ch;
        for (int i = 0; i < size; i++)
        {
            ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
            builder.Append(ch);
        }

        return builder.ToString();
    }

    private void SetUpReflectObjList()
    {
        for (int i = 0; i < 5000 ; i++)
        {
            ReflectClass1 reflectClass1 = new ReflectClass1();
            reflectClass1.Prop1 = RandomString(15);
            reflectClass1.Prop2 = RandomString(10);
            reflectClass1.Prop3 = RandomString(10);
            reflectClass1.Prop4 = RandomString(10);
            reflectClass1.Prop5 = RandomString(10);
            reflectClass1.Prop6 = RandomString(10);
            reflectClass1.Prop7 = RandomString(10);
            reflectClass1.Prop8 = RandomString(10);
            reflectClass1.Prop9 = RandomString(10);
            reflectClass1.Prop10 = RandomString(10);
            dataList.Add(reflectClass1);
        }
    }

    private void UseDelegateList()
    {
        Debug.WriteLine(string.Format(" Begin delegate performance test. item count = {0} start time: {1}",dataList.Count, DateTime.Now.ToLongTimeString()));
        for (int i = 0; i < dataList.Count; i++)
        {
            foreach (PropertyInfo propertyInfo in typeof(ReflectClass1).GetProperties())
            {
                if (delegateList.ContainsKey(propertyInfo.Name))
                {
                    Func<ReflectClass1, object> getPropDelegate = (Func<ReflectClass1, object>) delegateList[propertyInfo.Name];
                    Debug.Write(string.Format(" By delegates Object: {0} Property: {1} Value: {2}", i, propertyInfo.Name, getPropDelegate(dataList[i])));
                }
            }
        }
        Debug.WriteLine("");
        Debug.WriteLine(string.Format(" End delegate performance test. item count = {0} end time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
    }

    private void UseDirectReflection()
    {
        Debug.WriteLine(string.Format(" Begin direct reflection performance test. item count = {0} start time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
        for (int i = 0; i < dataList.Count; i++)
        {
            foreach (PropertyInfo propertyInfo in typeof(ReflectClass1).GetProperties())
            {
                if (propertyInfo == null) continue;
                {
                    Debug.Write(string.Format(" By reflection Object: {0} Property: {1} Value: {2}", i, propertyInfo.Name, propertyInfo.GetValue(dataList[i], null)));
                }
            }
        }
        Debug.WriteLine("");
        Debug.WriteLine(string.Format(" End direct reflection performance test. item count = {0} end time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
    }

    private void DirectOutputTest()
    {
        Debug.WriteLine(string.Format(" Begin direct output benchmark. item count = {0} start time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
        for (int i = 0; i < dataList.Count; i++)
        {

            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop1", dataList[i].Prop1));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop2", dataList[i].Prop2));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop3", dataList[i].Prop3));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop4", dataList[i].Prop4));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop5", dataList[i].Prop5));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop6", dataList[i].Prop6));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop7", dataList[i].Prop7));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop8", dataList[i].Prop8));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop9", dataList[i].Prop9));
            Debug.Write(string.Format(" direct output benchmark Object: {0} Property: {1} Value: {2}", i, "Prop10", dataList[i].Prop10));
        }
        Debug.WriteLine("");
        Debug.WriteLine(string.Format(" End direct output benchmark. item count = {0} end time: {1}", dataList.Count, DateTime.Now.ToLongTimeString()));
    }

最佳答案

2 件事:

  • 反射的性能在较新的运行时中变得更好,因为它是 .NET 语言的主要功能,并且因为人们非常关注静态和动态性能之间的差异。我假设您在 Framework v3.5 或 4.0 中运行它;如果您要在 Framework v2.0 中执行此代码,它的性能可能会更差。

  • 您所做的大部分工作都不是很“大量”地使用反射。动态属性调用有点繁重,但您所做的大部分工作只是获取信息。真正的重头戏是动态方法调用和动态实例创建。

假设您运行了以下测试。非常简单,唯一不同的是静态实例化和调用与反射:

public class ReflectionTest
    {
        public int Method1(){return 1;}
        public int Method2() { return 2; }
        public int Method3() { return 3; }
        public int Method4() { return 4; }
        public int Method5() { return 5; }
        public int Method6() { return 6; }
    }

    [Test]
    public void TestStatic()
    {
        for (var i = 1; i <= 100000; i++)
        {
            var reflectTest = new ReflectionTest();
            reflectTest.Method1();
            reflectTest.Method2();
            reflectTest.Method3();
            reflectTest.Method4();
            reflectTest.Method5();
            reflectTest.Method6();
        }
    }

    [Test]
    public void TestReflection()
    {
        var fullName = typeof (ReflectionTest).FullName;
        for (var i = 1; i <= 100000; i++)
        {
            var type = Assembly.GetExecutingAssembly().GetType(fullName, true, true);
            var reflectTest = Activator.CreateInstance(type);
            for (var j = 1; j <= 6; j++)
                type.GetMethod("Method" + j.ToString()).Invoke(reflectTest, null);
        }
    }

如果您想确保测试完全公平,您可以删除内部 for 循环并使用字符串文字“Method1”、“Method2”等调用 GetMethod 6 次。

反射测试不仅动态调用方法,它搜索 list 以找到并实例化一个 Type 对象,然后动态实例化来自 Type 的实际对象,在该对象上动态调用方法。我敢打赌,如果您同时运行这两个测试,那么第二个测试的表现会差得多。此外,探索将参数传递给这些方法;首先,您必须找到正确的重载,然后反射调用采用一个 Object[] 数组,该数组将装箱和拆箱方法的任何值类型参数,进一步减慢反射算法的速度。

简而言之,反射会比静态算法表现更差;然而,在提高其性能方面取得了长足的进步,因此从 .NET 4.0 开始,智能编写的动态算法与对应的静态算法相比并没有那么大的损失,使得反射在需要时更加可行。

编辑:并排运行上述 2 个测试后,相对差异很大:100k 次迭代的静态算法 0.07 秒,反射高达 2.12 秒。反射实例化/调用比静态实例化/调用花费的时间长 30 倍。但是,差异需要 100,000 次迭代才能显着; Debug.WriteLine 语句在我最初的测试中是迄今为止这两个测试中最慢的部分。

关于c# - 反射测试未显示预期数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5239310/

相关文章:

c# - 为什么在 Debug模式下会得到随机数?

C#MVC : Optional columns in grid (foreach)

performance - 如何使用恒定吞吐量计时器指定一个小时内每秒最多 4 个并发用户

c++ - 重载类型转换为 bool 和效率

java - 所有非垃圾收集的对象最终都是静态引用的吗?

java - 关于反射包的问题

C# - 是否可以使用 RSA SHA512 对哈希进行签名?

c# - C# 中的 "protected"方法?

c++ - 初始化为一个值在性能上与赋值相比如何?

scala - 是否可以在不使用反射的Scala中实现 `??`(C#中的空合并运算符)?