Проблемы со скоростью работы с делегатами и структурами

#.net #performance #delegates #struct

#.net #Производительность #делегаты #структура

Вопрос:

Я столкнулся с некоторыми проблемами скорости в отношении структур и делегатов — возьмите следующий код консольного приложения:

 public delegate string StringGetter();
public class LocalString
{
    public LocalString(string value)
    {
        this.value = value;
    }

    public StringGetter Getter
    {
        get
        {
            return new StringGetter(this.GetValue);
        }
    }

    private string GetValue()
    {
        return value;
    }

    private string value;
}


class Program
{
    static void Main(string[] args)
    {
        var start = DateTime.Now;
        for (int i = 0; i < 2000000; i  )
        {
            var val = new LocalString( "hello World" );
            val.Getter();
        }
        Console.WriteLine((DateTime.Now - start).TotalMilliseconds);
        Console.ReadKey();
    }
}
  

При выполнении на моем компьютере это занимает ~ 1,8 секунды…Если я изменяю структуру на класс, она выполняется за ~ 0,1 секунды. Я взглянул на базовый код ассемблера и код ROTOR с открытым исходным кодом, чтобы понять, почему, и есть некоторый специальный код для делегатов, у которых есть цель struct, которая, как я предполагаю, предназначена для обработки упаковки и распаковки в функции MethodDesc * COMDelegate::GetDelegateCtor (TypeHandle delegateType, MethodDesc * pTargetMethod, DelegateCtorArgs * pCtorData).

Еще один момент — если вы создадите это в VS2008, ориентированном на .net 3.5, приложение будет работать быстрее, чем если вы запустите его в VS2010, ориентированном на .net 3.5. Я не понял, почему это так.

Любые комментарии / лучшее просвещение приветствовались бы…

С уважением, Ли

Комментарии:

1. Пример частично надуман (некоторые взяты из реального кода) просто для повторения проблемы. По общему признанию, я мог бы уменьшить его еще больше, но он компилируется и запускается.

2. @leppie: Я предлагаю вам на самом деле попробовать скомпилировать это, а затем пересмотреть свою позицию…

3. @WillemvanRumpt: Я признаю. 🙂 Вопрос, однако, в том, почему нужно делать new StringGetter(somestringgetter) вместо instance.Getter ? На самом деле я нахожу странным, что у делегата есть такой конструктор (принимающий делегат того же типа). Я не вижу смысла в такой «магии».

4. Чтобы прояснить мою точку зрения: var k = new Action(new Action( new Action (K))); это действительный и совершенно ужасный код.

5. @leppie: О, мы можем согласиться с высоким значением скриптовости кода на доске 🙂

Ответ №1:

На этот вопрос трудно ответить точно, код поддержки CLR для делегатов — крепкий орешек. Мое лучшее предположение — это накладные расходы, необходимые для отмены значения struct. Вызов делегата выполняется через заглушку, которая сначала вводит значение, чтобы можно было вызвать метод экземпляра. После вызова любые побочные эффекты метода необходимо скопировать обратно в исходную структуру. Это дорого по сравнению с простым вызовом метода экземпляра ссылочного типа, они очень быстры. Я не видел никаких доказательств для проверки жизнеспособности значения struct, немного странно, но вполне может быть где-то там.

Комментарии:

1. Звучит как хорошее объяснение для меня! По-прежнему остается вопрос о том, почему сборка exe с 2010 .net v3.5 выполняется немного медленнее, чем сборка, созданная VS2008 .net v3.5… В любом случае, хороший ответ, приветствия!

2. Если вы уверены, что они оба выполняются на одном и том же. Net, тогда разница может быть только в IL. Вы можете легко проверить это с помощью какого-нибудь инструмента, такого как Reflector или ildasm.