Почему последующий прямой вызов метода намного быстрее, чем первый вызов? - PullRequest
7 голосов
/ 21 июня 2011

Как объяснено в этой статье MSDN , при использовании .NET Reflection API, такого как InvokeMember, первый вызов выполняется намного больше времени, чем последующие вызовы из-за кэширования метаданных.

Когда я тестировал прямой вызов метода без использования Reflection, я также вижу похожий эффект как в Mono, так и в .NET 4.

Первое число является результатом операции, а второе число после «-» - это время, затраченное на эту операцию в мс. Я использовал знак «<-» для идентификации первого вызова метода. </p>

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

Почему это? Я понимаю, что первый вызов может быть медленнее, но в 50 раз медленнее, чем я ожидал.

Прилагается исходный код для получения этого результата.

библиотека

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();
    }
}

1 Ответ

13 голосов
/ 21 июня 2011

Это связано с методом компиляции Just In Time (JIT) , который используется для приложений .NET.Байт-код MSIL преобразуется в машинный код один раз компилятором JIT, и последующие исполнения этого кода выполняются намного быстрее, потому что собственная версия была сгенерирована и кэширована.

Вы платите одноразовый штрафкогда вы запускаете свой код, но JIT-компилятор может также выполнить оптимизацию для текущей архитектуры, которую нельзя было бы выполнить, если код был встроенным с самого начала.Однако вы можете форсировать JIT-пропуск, вызвав RuntimeHelpers.PrepareMethod.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...