为什么后来直接的方法调用比第一次调用快得多?快得多、后来、直接、方法

2023-09-04 01:01:46 作者:〆淳化滴神〆

由于在这个MSDN文章,当一个人使用。 NET反射API,如 InvokeMember ,第一次调用花费更多的时间比因为元数据缓存的后续调用运行。

当我测试了直接的方法调用,而无需使用反射,我也看到了类似的效果无论在单声道和.NET 4

第一个数字是该操作的结果,并经过第二个数字 - 是花费此操作以毫秒的时间。我用'< - '标志,以确定第一个方法调用

  300  -  0.192<  - 
300  -  0.004
300  -  0.004
-100  -  0.096<  - 
-100  -  0.004
-100  -  0.004
 

这是为什么?我可以理解,第一个电话可以比较慢,但是慢50倍是不是我所期待。

附件是源$ C ​​$ C,以得到这样的结果。

 命名空间MyClass的
{
    公共类计算器
    {
        公众诠释值1 {获得;组;}
        公众诠释值2 {获得;组;}
        公共计算器()
        {
            值1 = 100;
            值2 = 200;
        }

        公众诠释添加(INT VAL1,INT将val2)
        {
            值1 = VAL1;值2 = val2的;
            返回值1 +值2;
        }

        公众诠释子(INT VAL1,INT将val2)
        {
            值1 = VAL1;值2 = val2的;
            返回值1  - 值2;
        }
    }
}
 
你的 高度 ,决定了孩子的 起点

code调用这个库

  // http://msdn.microsoft.com/en-us/magazine/cc163759.aspx
使用系统;
使用System.IO;
使用的System.Reflection;
使用System.Diagnostics程序;
使用System.Collections.Generic;
使用MyClass的;

类TestOne
{
    静态无效DirectTest()
    {
        秒表SW;
        计算器T =新计算器();

        SW = Stopwatch.StartNew();
        INT值1 = t.Add(100,200);
        sw.Stop();
        双时间1 = sw.Elapsed.TotalMilliseconds;

        SW = Stopwatch.StartNew();
        INT值2 = t.Add(100,200);
        sw.Stop();
        双时间2 = sw.Elapsed.TotalMilliseconds;

        SW = Stopwatch.StartNew();
        INT值3 = t.Add(100,200);
        sw.Stop();
        双时间3 = sw.Elapsed.TotalMilliseconds;

        Console.WriteLine({0}  -  {1},值1,时间1);
        Console.WriteLine({0}  -  {1},值2,时间2);
        Console.WriteLine({0}  -  {1},值3,时间3);

        SW = Stopwatch.StartNew();
        值1 = t.Sub(100,200);
        sw.Stop();
        时间1 = sw.Elapsed.TotalMilliseconds;

        SW = Stopwatch.StartNew();
        值2 = t.Sub(100,200);
        sw.Stop();
        时间2 = sw.Elapsed.TotalMilliseconds;

        SW = Stopwatch.StartNew();
        值3 = t.Sub(100,200);
        sw.Stop();
        时间3 = sw.Elapsed.TotalMilliseconds;

        Console.WriteLine({0}  -  {1},值1,时间1);
        Console.WriteLine({0}  -  {1},值2,时间2);
        Console.WriteLine({0}  -  {1},值3,时间3);
    }
    静态无效的主要()
    {
        DirectTest();
        DirectTest();
    }
}
 

解决方案

这是因为准时(中JIT)是用于.NET应用程序的编制方法。该 MSIL字节code 是由JIT编译器和$后续的执行转换为机器code一次C $ c是更快,因为本机版本已经生成并缓存。

您支付一次点球,当您运行code,但JIT编译器还可以进行优化的,它不能被执行,如果code组原生从一开始就在当前的体系结构。你可以通过调用RuntimeHelpers.$p$ppareMethod.

As is explained in this MSDN article, when one uses .NET Reflection API such as InvokeMember, the first call takes much more time to run than the subsequent calls because of caching of metadata.

When I tested the direct method call without using Reflection, I also see the similar effect both on Mono and .NET 4.

The first number is the result of the operation, and the second number after '-' is the time spent for this operation in ms. I used '<--' mark to identify the first method call.

300 - 0.192 <--
300 - 0.004
300 - 0.004
-100 - 0.096 <--
-100 - 0.004
-100 - 0.004

Why is this? I can understand that the first call can be slower, but 50x slower is not what I expected.

Attached is the source code to get this result.

library

namespace MyClass
{
    public class Calculator
    {
        public int Value1 {get; set;}
        public int Value2 {get; set;}
        public Calculator()
        {
            Value1 = 100;
            Value2 = 200;
        }

        public int Add(int val1, int val2)
        {
            Value1 = val1; Value2 = val2;
            return Value1 + Value2;
        }

        public int Sub(int val1, int val2)
        {
            Value1 = val1; Value2 = val2;
            return Value1 - Value2;
        }
    }
}

Code to call this library

// http://msdn.microsoft.com/en-us/magazine/cc163759.aspx
using System;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;
using MyClass;

class TestOne
{
    static void DirectTest()
    {
        Stopwatch sw;
        Calculator t = new Calculator();

        sw = Stopwatch.StartNew();
        int value1 = t.Add(100,200);
        sw.Stop();
        double time1 = sw.Elapsed.TotalMilliseconds;

        sw = Stopwatch.StartNew();
        int value2 = t.Add(100,200);   
        sw.Stop();
        double time2 = sw.Elapsed.TotalMilliseconds;

        sw = Stopwatch.StartNew();
        int value3 = t.Add(100,200); 
        sw.Stop();
        double time3 = sw.Elapsed.TotalMilliseconds;

        Console.WriteLine("{0} - {1}", value1, time1);
        Console.WriteLine("{0} - {1}", value2, time2);
        Console.WriteLine("{0} - {1}", value3, time3);

        sw = Stopwatch.StartNew();
        value1 = t.Sub(100,200);
        sw.Stop();
        time1 = sw.Elapsed.TotalMilliseconds;

        sw = Stopwatch.StartNew();
        value2 = t.Sub(100,200);  
        sw.Stop();
        time2 = sw.Elapsed.TotalMilliseconds;

        sw = Stopwatch.StartNew();
        value3 =  t.Sub(100,200); 
        sw.Stop();
        time3 = sw.Elapsed.TotalMilliseconds;

        Console.WriteLine("{0} - {1}", value1, time1);
        Console.WriteLine("{0} - {1}", value2, time2);
        Console.WriteLine("{0} - {1}", value3, time3);
    }
    static void Main()
    {
        DirectTest();
        DirectTest();
    }
}

解决方案

This is because of the Just In Time (JIT) compilation method that is used for .NET apps. The MSIL bytecode is translated to machine code once by the JIT compiler and subsequent executions of that code are much faster because the native version has been generated and cached.

You pay a one time penalty when you run your code, but the JIT compiler can also perform optimizations for the current architecture that it could not be performed if the code were native from the get-go. You can however force a JIT pass by calling RuntimeHelpers.PrepareMethod.