C# 为什么后续直接方法调用比第一次调用快得多?
如中所述,当使用.NET反射API(如C# 为什么后续直接方法调用比第一次调用快得多?,c#,.net,performance,C#,.net,Performance,如中所述,当使用.NET反射API(如InvokeMember)时,由于元数据的缓存,第一个调用比后续调用运行的时间要长得多 当我在不使用反射的情况下测试direct方法调用时,我在Mono和.NET4上也看到了类似的效果 第一个数字是该操作的结果,“-”之后的第二个数字是该操作在毫秒内花费的时间。我使用了“这是因为.NET应用程序使用的编译方法。JIT编译器会将代码转换为机器代码一次,并且该代码的后续执行速度要快得多,因为已经生成并缓存了本机版本 当您运行代码时,您需要支付一次性的罚款,但是J
InvokeMember
)时,由于元数据的缓存,第一个调用比后续调用运行的时间要长得多
当我在不使用反射的情况下测试direct方法调用时,我在Mono和.NET4上也看到了类似的效果
第一个数字是该操作的结果,“-”之后的第二个数字是该操作在毫秒内花费的时间。我使用了“这是因为.NET应用程序使用的编译方法。JIT编译器会将代码转换为机器代码一次,并且该代码的后续执行速度要快得多,因为已经生成并缓存了本机版本
当您运行代码时,您需要支付一次性的罚款,但是JIT编译器也可以对当前体系结构执行优化,如果代码是从一开始就固有的,则无法执行这些优化。但是,您可以通过调用来强制JIT传递不要认为第一次调用的速度慢了50倍,而要想后续调用的速度快了50倍。@Travis Gockel:是的,但在某些情况下,您真正关心的是第一次传递。当然有很多方法可以解决这个问题。多个副本,包括任何可能的副本,很可能是外部CPU压力。它只是一个(非常)糟糕的基准。@Henk Holterman:不,它不可能是任何东西,它肯定是JIT编译过程。可能是,但不可能是全部答案。在第一个秒表之前调用这些方法很容易测试。@Henk Holterman:“不可能是全部答案。”-为什么?你还有什么建议?对我来说,这就像是JIT通行证。你还有其他建议吗?@Henk Holterman:嗯,我可以看到他的代码,所以我们知道那里没有线程问题。当然,Windows不是RTOS,但这些数字不会说谎,而且非常可重复。调度问题不会导致如此大的差异,除非机器完全受CPU限制,在这种情况下,我们不会看到如此可重复的数字。我花了相当多的时间在运行.NET应用程序中的CPU瓶颈,我在这里没有看到任何迹象表明这只是第一次JIT传递的结果。你是否也在对双线方法的单次调用周围放置秒表?@Henk Holterman:这会有什么区别?如果有任何问题,代码将以秒表分辨率的速度执行,但鉴于
.194ms
结果,这里的情况并非如此。
300 - 0.192 <--
300 - 0.004
300 - 0.004
-100 - 0.096 <--
-100 - 0.004
-100 - 0.004
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;
}
}
}
// 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();
}
}