2017-01-29 27 views
9

Пусть я следующие программы:Weird поведение компилятора C# из-за кэширования делегатом

static void SomeMethod(Func<int, int> otherMethod) 
{ 
    otherMethod(1); 
} 

static int OtherMethod(int x) 
{ 
    return x; 
} 

static void Main(string[] args) 
{ 
    SomeMethod(OtherMethod); 
    SomeMethod(x => OtherMethod(x)); 
    SomeMethod(x => OtherMethod(x)); 
} 

Я не могу понять, скомпилированный код иль (он использует слишком дополнительный код). Вот упрощенная версия:

class C 
{ 
    public static C c; 
    public static Func<int, int> foo; 
    public static Func<int, int> foo1; 
    static C() 
    { 
     c = new C(); 
    } 
    C(){} 
    public int b(int x) 
    { 
     return OtherMethod(x); 
    } 
    public int b1(int x) 
    { 
     return OtherMethod(x); 
    } 
} 

static void Main() 
{ 
    SomeMethod(new Func<int, int>(OtherMethod)); 
    if (C.foo != null) 
     SomeMethod(C.foo) 
    else 
    { 
     C.foo = new Func<int, int>(c, C.b) 
     SomeMethod(C.foo); 
    } 
    if (C.foo1 != null) 
     SomeMethod(C.foo1) 
    else 
    { 
     C.foo1 = new Func<int, int>(c, C.b1) 
     SomeMethod(C.foo1); 
    } 
} 

Почему компилятор создавать не статические методы равных b/b1? Равное означает, что они имеют один и тот же код

+0

SomeMethod (x => OtherMethod (x)); также имеет значение с 1 – becike

+0

@becike, да, конечно. Я с тобой согласен. Но реализация кажется weird – LmTinyToon

+0

, иначе я не уверен, что вы хотите, поскольку этот код не компилируется, поэтому вы пропустили пару частей – becike

ответ

17

Ваш вопрос: почему компилятор не понимает, что две линии

SomeMethod(x => OtherMethod(x)); 
SomeMethod(x => OtherMethod(x)); 

Одинаковы и писать это как

if (delegate is not created) 
    create the delegate and stash it away 
SomeMethod(the delegate); 
SomeMethod(the delegate); 

? Хорошо, позвольте мне ответить на этот вопрос несколькими способами.

Прежде всего, является ли компилятор разрешенным, чтобы сделать эту оптимизацию? Да. В спецификации указано, что компилятор C# равен , разрешенному, чтобы сделать два лямбда, которые выполняют одну и ту же вещь в одном делетете. И на самом деле вы можете видеть, что он уже частично выполняет эту оптимизацию: он создает каждый делегат один раз и сохраняет его, так что ему не нужно создавать его снова позже, когда код вызывается снова. Обратите внимание, что это пустая трата памяти в случае, когда код вызывается только один раз.

Во-вторых, нужен ли компилятор , чтобы сделать оптимизацию кеширования? Нет. В спецификации указано, что компилятор только разрешен, чтобы сделать оптимизацию, но не требуется.

Требуется ли компилятор , чтобы сделать оптимизацию? Очевидно, нет, потому что это не так. Это разрешено, и, возможно, будущая версия компилятора будет. Компилятор является открытым исходным кодом; если вы заботитесь об этой оптимизации, напишите об этом и отправьте запрос на перенос.

В-третьих, это возможно, чтобы сделать оптимизацию, которую вы хотите? Да. Компилятор мог взять все пары лямбда, которые появляются в одном и том же методе, скомпилировать их во внутреннем древовидном формате и провести сравнение дерева, чтобы увидеть, есть ли у них одинаковый контент, а затем создать то же самое статическое поле поддержки для обоих.

Итак, теперь у нас есть ситуация: компилятор разрешил, чтобы сделать определенную оптимизацию, и это не так. И вы спросили «почему нет»? Это простой вопрос, чтобы ответить: все оптимизаций не выполняются до тех пор, пока кто-то тратит много времени и усилий на:

  • Тщательно дизайн оптимизации: при каких именно условиях является оптимизация срабатывает и не срабатывает? Как вообще должна быть оптимизация? Вы предположили, что они обнаруживают подобные лямбда-тела, но зачем останавливаться там? У вас есть два идентичных оператора кода, так почему бы не сгенерировать код для этих операторов один раз вместо двух? Что делать, если у вас была повторная группа заявлений ?Здесь есть огромная проектная работа.
  • В частности, важным аспектом конструкции является то, что пользователь может разумно выполнить оптимизацию «вручную», сохраняя при этом код читаемым. В этом случае, да, они могли бы, легко. Просто назначьте дублируемую лямбду переменной и затем используйте переменную. Оптимизация, которая автоматически делает то, что пользователь, который заботился, мог сделать легко, на самом деле не очень интересная или убедительная оптимизация.
  • Ваши примеры тривиальны; реального кода нет. Что делает ваш предлагаемый дизайн с идентичными вложенными lambdas? И так далее.
  • Выполняет ли ваша оптимизация поведение кода в отладчике, чтобы «выглядеть странным»? Вероятно, вы заметили, что при отладке кода, который был скомпилирован с оптимизацией, отладчик, похоже, ведет себя странно; это потому, что теперь нет четкого сопоставления между сгенерированным кодом и исходным кодом. Ваша оптимизация делает это хуже? Является ли он приемлемым для пользователей? Должен ли отладчик быть в курсе оптимизации? Если это так, вам придется изменить отладчик. В этом случае, вероятно, нет, но это вопросы, которые вы должны задать и ответить.
  • Получить дизайн, рассмотренный экспертами; это занимает свое время и, скорее всего, приведет к изменениям в дизайне.
  • Сделать оценки плюсов и минусов оптимизации. Оптимизация часто имеет скрытые затраты, такие как утечка памяти, о которой я упоминал ранее. В частности, оптимизации часто исключают другие оптимизации, которые могут быть лучше.
  • Оцените общую экономию во всем мире этой оптимизации. Действительно ли оптимизация влияет на реальный код? Изменяет ли он правильность этого кода? Есть ли какой-либо производственный код в любой точке мира, который нарушит эту оптимизацию и заставит технический директора компании X вызывать технический директор Microsoft с требованием исправить? Если ответ «да», возможно, вам может понадобиться не делать эту оптимизацию. C# не игрушка. Ежедневно миллионы и миллионы людей зависят от правильной работы.
  • Какова оценочная нагрузка на оптимизацию на время компиляции? Компиляция не должна происходить между нажатиями клавиш, но она должна быть довольно быстрой. Все, что вводит суперлинейный алгоритм в общий путь кода в компиляторе, будет неприемлемым. Можете ли вы реализовать свою оптимизацию, чтобы она была линейной по размеру кода? Обратите внимание, что алгоритм, который я набросал ранее, - сравнить все пары - является суперлинейным по размеру кода. (Упражнение: что в худшем случае асимптотическая производительность при сравнении дерева по всем парам лямбда?)
  • На самом деле реализовать оптимизацию. Я призываю вас сделать это.
  • Проверьте оптимизацию; действительно ли он дает лучший код? На какой метрике? Оптимизация, которая не приводит к изменению какой-либо метрики, не является оптимизацией.
  • Зарегистрируйтесь, чтобы исправить ошибки в оптимизации навсегда.

Оптимизация, которую вы хотите, просто не соответствует полосе. Никто не пишет такой код. Если они это сделали, и они заботились о том, чтобы он продублировал объект, они могли легко исправить его сами. Таким образом, оптимизация оптимизирует код, который не существует, чтобы получить «выигрыш», который является построением одного объекта среди миллионов и миллионов объектов, которые программа выделит. Не стоит.

Но опять же, если вы так думаете, продолжайте и реализуйте его и отправьте запрос на тяну. Не забудьте представить результаты проведенных мной расследований, потому что это настоящая работа. Реализация, как правило, является наименьшей частью общего усилия, затраченного на функцию; поэтому C# - успешный язык.

+1

Отличный ответ! Я полностью согласен. – LmTinyToon

 Смежные вопросы

  • Нет связанных вопросов^_^