Могу ли я остановить .NET 4, выполняющую устранение хвостовых вызовов? - PullRequest
13 голосов
/ 18 апреля 2011

Мы находимся в процессе миграции приложения на .NET 4.0 (с версии 3.5). Одна из проблем, с которыми мы сталкиваемся, воспроизводима только в очень специфических условиях:

  • Только в сборке выпуска
  • Только с включенной оптимизацией и / или отладочной информацией, установленной только для pdb.

Я имею в виду, что если я отключу оптимизацию и , установив отладочную информацию на полную, проблема исчезнет.

Рассматриваемый код прекрасно работает в .NET 3.5, в режиме выпуска с включенной оптимизацией и т. Д. И работает в течение длительного времени.

Я действительно не хочу предполагать, что в компиляторе C # есть ошибка, поэтому на самом деле у меня вопрос, есть ли какие-нибудь методы, которые я могу использовать для отслеживания того, что мы можем делать неправильно, чтобы вызвать неправильную оптимизацию? *

Я пытаюсь сузить эту проблему до небольшого тестового примера, чтобы я мог опубликовать здесь некоторый код.

Edit:

Я отследил проблему до следующего:

У нас есть этот код в конструкторе формы:

public ConnectionForm()
{
    LocalControlUtil.Configure("ConnectionForm", "Username", usernameLabel);
    LocalControlUtil.Configure("ConnectionForm", "Password", passwordLabel);
    LocalControlUtil.Configure("ConnectionForm", "Domain", domainLabel);
    LocalControlUtil.Configure("ConnectionForm", "Cancel", cancelButton);
    LocalControlUtil.Configure("ConnectionForm", "OK", okButton);
}

Эти вызовы относятся к некоторому пользовательскому коду локализации. Конструктор для этой формы вызывается из другой сборки. Метод LocalControlUtil.Configure вызывает Assembly.GetCallingAssembly(), который возвращает правильное значение для всех вышеперечисленных вызовов, , кроме последнего .

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

Я предполагаю, что это JIT, вставляющий последний вызов метода в место, где был вызван конструктор (в другой сборке). Добавление [MethodImpl(MethodImplOptions.NoInlining)] в конструктор выше устраняет проблему.

Кто-нибудь знает, почему это происходит? Мне кажется странным, что только последняя строка может быть вставлена. Это новое поведение в .NET 4.0?

Редактировать 2:

Я теперь сузил это до устранения хвостового вызова, я полагаю, вызванного новым материалом хвостового вызова в .NET 4 .

В приведенном выше коде последний вызов LocalControlUtil.Configure в конструкторе исключается и помещается в вызывающий метод, который находится в другой сборке. Поскольку метод вызывает Assembly.GetCallingAssembly, мы не вернем правильную сборку.

Есть ли какой-нибудь способ остановить компилятор (или JIT или что-то еще) от устранения хвостового вызова?

Ответы [ 3 ]

4 голосов
/ 25 мая 2011

Я бы просто добавил это в комментарий, если бы он не был слишком длинным, но вы пробовали:

public ConnectionForm()
{
  try
  {
    LocalControlUtil.Configure("ConnectionForm", "Username", usernameLabel);
    LocalControlUtil.Configure("ConnectionForm", "Password", passwordLabel);
    LocalControlUtil.Configure("ConnectionForm", "Domain", domainLabel);
    LocalControlUtil.Configure("ConnectionForm", "Cancel", cancelButton);
    LocalControlUtil.Configure("ConnectionForm", "OK", okButton);
  }
  catch
  {
    throw;
  }
}

Тот факт, что генерируется любое исключение, делает это в значительной степени нулевым изменением с точки зрения написанного кода, но границы try-catch часто предотвращают оптимизацию со стороны компилятора и джиттера.

1 голос
/ 18 апреля 2011

Я думаю, что вы уже на правильном пути для поиска проблемы ... Сужение до самого маленького контрольного примера - почти всегда отличный первый шаг.

Следующим будет сравнение IL, сгенерированного для каждой версии, в чем-то вроде Reflector .Чтение IL не является тривиальным (если у вас нет достаточного опыта), поэтому очень важно свести к минимуму количество кода, который вам нужно просмотреть.и если проблема не видна на уровне IL, это может быть на уровне JIT (Just In Time), который значительно сложнее отладить.Мне никогда не приходилось работать на этом уровне, поэтому я не имею никакого представления об этой части проблемы (если она доходит до этого).

0 голосов
/ 06 марта 2012

Нет, вы не можете.

.NET 4.0 оптимизирует больше хвостовых вызовов, чем 3,5, и это хорошо. Наш код был безумно глупым.

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