为什么反射比对象初始化更快?

Kyl*_*Ren 3 c# reflection

  1. 对不起,非计算机科学术语(自学)
  2. 抱歉,术语不正确(任何愿意编辑的人,请做(自学))

我对所搜索的术语的知识有限,因此我进行了尽可能多的搜索,但是我找不到并回答我所追求的问题。

我很惊讶地发现,用数据填充对象Reflection比使用更快object Initialization

为此,我制作了一个测试控制台应用程序。首先是测试班

class TestClass
    {
        public string PropertyOne { get; set; }
        public string PropertyTwo { get; set; }
        public string PropertyThree { get; set; }
        public string PropertyFour { get; set; }
        public string PropertyFive { get; set; }
        public string PropertySix { get; set; }
        public string PropertySeven { get; set; }
        public string PropertyEight { get; set; }
        public string PropertyNine { get; set; }
        public string PropertyTen { get; set; }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后是获取数据的方法,首先是反射

public static void ReflectionTest()
    {
        for (int i = 0; i < 10000000; i++)
        {
            TestClass testClass = new TestClass();
            Type type = testClass.GetType();
            PropertyInfo[] properties = type.GetProperties();
            foreach (var propertyInfo in properties)
            {
                switch (propertyInfo.Name)
                {
                    case nameof(testClass.PropertyOne):
                        propertyInfo.SetValue(testClass, "PropertyOne" + i);
                        break;
                    case nameof(testClass.PropertyTwo):
                        propertyInfo.SetValue(testClass, "PropertyTwo" + i);
                        break;
                    case nameof(testClass.PropertyThree):
                        propertyInfo.SetValue(testClass, "PropertyThree" + i);
                        break;
                    case nameof(testClass.PropertyFour):
                        propertyInfo.SetValue(testClass, "PropertyFour" + i);
                        break;
                    case nameof(testClass.PropertyFive):
                        propertyInfo.SetValue(testClass, "PropertyFive)" + i);
                        break;
                    case nameof(testClass.PropertySix):
                        propertyInfo.SetValue(testClass, "PropertySix" + i);
                        break;
                    case nameof(testClass.PropertySeven):
                        propertyInfo.SetValue(testClass, "PropertySeven" + i);
                        break;
                    case nameof(testClass.PropertyEight):
                        propertyInfo.SetValue(testClass, "PropertyEight" + i);
                        break;
                    case nameof(testClass.PropertyNine):
                        propertyInfo.SetValue(testClass, "PropertyNine" + i);
                        break;
                    case nameof(testClass.PropertyTen):
                        propertyInfo.SetValue(testClass, "PropertyTen" + i);
                        break;
                }                    
            }
            TestClasses.Add(testClass);
        }
    }
Run Code Online (Sandbox Code Playgroud)

接下来是对象的初始化

public static void InitializationTest()
    {
        for (int i = 0; i < 10000000; i++)
        {
            TestClass testClass = new TestClass
            {
                PropertyOne = "PropertyOne" + i,
                PropertyTwo = "PropertyTwo" + i,
                PropertyThree = "PropertyThree" + i,
                PropertyFour = "PropertyFour" + i,
                PropertyFive = "PropertyFive)" + i,
                PropertySix = "PropertySix" + i,
                PropertySeven = "PropertySeven" + i,
                PropertyEight = "PropertyEight" + i,
                PropertyNine = "PropertyNine" + i,
                PropertyTen = "PropertyTen" + i
            };
            TestClasses.Add(testClass);
        }
    }
Run Code Online (Sandbox Code Playgroud)

和测试代码

static List<TestClass> TestClasses { get; set; } = new List<TestClass>();

    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        ReflectionTest();
        Console.WriteLine($"Reflection Test time: {sw.Elapsed}");
        sw.Reset();
        sw.Start();
        InitializationTest();
        Console.WriteLine($"Initialization Test time: {sw.Elapsed}");
        sw.Stop();
    }
Run Code Online (Sandbox Code Playgroud)

使用此代码,使用对象初始化的反射速度提高了20%。这是什么原因呢?

编辑:添加TestClasses.Clear();到代码中,表明“对象初始化”几乎快了一倍。感谢您的答复和评论。

ASh*_*ASh 5

这种测量没有意义-否则每个人都将使用反射而不是对象实例。

结果令人困惑的主要原因是所有项目都被添加到同一列表中。以不同的顺序调用测试并获得InitializationTest更好的性能。

static void Main(string[] args)
{
    Stopwatch sw = new Stopwatch();

    sw.Start();
    InitializationTest();
    Console.WriteLine($"Initialization Test time: {sw.Elapsed}");
    sw.Stop();
    sw.Reset();

    sw.Start();
    ReflectionTest();
    Console.WriteLine($"Reflection Test time: {sw.Elapsed}");
    sw.Stop();
    sw.Reset();

    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)

正如Matthew Watson在评论中所言:“更好的是,不要将所有列表用作计时的一部分。我们应该计时的时间是创建对象需要多长时间-而不是填充一个非常长的时间。大名单。”


反思测试包括不必要的费用:重复GetProperties发票,交换机中的测试条件

public static void ReflectionTest()
{
    PropertyInfo[] properties = typeof(TestClass).GetProperties();
    for (int i = 0; i < 10000000; i++)
    {
        TestClass testClass = new TestClass();
        foreach (var propertyInfo in properties)
        {
            propertyInfo.SetValue(testClass, propertyInfo.Name + i);
        }
        TestClasses.Add(testClass);
    }
}
Run Code Online (Sandbox Code Playgroud)


can*_*on7 5

基准测试很难。始终使用BenchmarkDotNet

我使用BenchmarkDotNet重写了您的测试,并得到了以下结果:

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18362
Intel Core i5-3317U CPU 1.70GHz (Ivy Bridge), 1 CPU, 4 logical and 2 physical cores
.NET Core SDK=3.0.100-preview6-012264
  [Host]     : .NET Core 3.0.0 (CoreCLR 4.700.19.30373, CoreFX 4.700.19.30308), X64 RyuJIT
  DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.30373, CoreFX 4.700.19.30308), X64 RyuJIT
Run Code Online (Sandbox Code Playgroud)
|     Method |        Mean |     Error |    StdDev |
|----------- |-------------|-----------|-----------|
|   Instance |    41.61 ns |  0.309 ns |  0.274 ns |
| Reflection | 3,321.40 ns | 28.379 ns | 25.157 ns |
Run Code Online (Sandbox Code Playgroud)

这清楚地表明您的反射代码比使用对象初始化程序慢80倍左右。

@ASh的答案是关于您的基准测试为什么有缺陷的原因,因此,我在这里不再重复,请参阅他们的答案。

基准代码:

public class Benchmark
{
    [Benchmark]
    public TestClass Instance()
    {
        TestClass testClass = new TestClass
        {
            PropertyOne = "PropertyOne",
            PropertyTwo = "PropertyTwo",
            PropertyThree = "PropertyThree",
            PropertyFour = "PropertyFour",
            PropertyFive = "PropertyFive)",
            PropertySix = "PropertySix",
            PropertySeven = "PropertySeven",
            PropertyEight = "PropertyEight",
            PropertyNine = "PropertyNine",
            PropertyTen = "PropertyTen"
        };
        return testClass;
    }

    [Benchmark]
    public TestClass Reflection()
    {
        TestClass testClass = new TestClass();
        Type type = testClass.GetType();
        PropertyInfo[] properties = type.GetProperties();
        foreach (var propertyInfo in properties)
        {
            switch (propertyInfo.Name)
            {
                case nameof(testClass.PropertyOne) :
                    propertyInfo.SetValue(testClass, "PropertyOne");
                    break;
                case nameof(testClass.PropertyTwo) :
                    propertyInfo.SetValue(testClass, "PropertyTwo");
                    break;
                case nameof(testClass.PropertyThree) :
                    propertyInfo.SetValue(testClass, "PropertyThree");
                    break;
                case nameof(testClass.PropertyFour) :
                    propertyInfo.SetValue(testClass, "PropertyFour");
                    break;
                case nameof(testClass.PropertyFive) :
                    propertyInfo.SetValue(testClass, "PropertyFive");
                    break;
                case nameof(testClass.PropertySix) :
                    propertyInfo.SetValue(testClass, "PropertySix");
                    break;
                case nameof(testClass.PropertySeven) :
                    propertyInfo.SetValue(testClass, "PropertySeven");
                    break;
                case nameof(testClass.PropertyEight) :
                    propertyInfo.SetValue(testClass, "PropertyEight");
                    break;
                case nameof(testClass.PropertyNine) :
                    propertyInfo.SetValue(testClass, "PropertyNine");
                    break;
                case nameof(testClass.PropertyTen) :
                    propertyInfo.SetValue(testClass, "PropertyTen");
                    break;
            }
        }
        return testClass;
    }
}

class Program
{
    public static void Main()
    {
        var summary = BenchmarkRunner.Run(typeof(Program).Assembly);
    }
}
Run Code Online (Sandbox Code Playgroud)