Код ведет себя по-разному в режиме Release vs Debug
У нас есть некоторые модульные тесты, которые терпят неудачу при запуске в режиме выпуска против режима отладки. Если я подключу отладчик в режиме выпуска, тесты пройдут. Здесь слишком много кода для публикации, поэтому я просто ищу рекомендации по отладке проблем в режиме выпуска. Я проверил:
- DEBUG и RELEASE директивы препроцессора, но я не нашел ни одного.
- Условные Методы
решение: в этом случае это потому, что я был сравнение переменных с плавающей запятой для равенства. Я не мог изменить поплавки на decimal без крупного рефакторинга, поэтому я добавил метод расширения:
public static class FloatExtension
{
public static bool AlmostEquals(this float f1, float f2, float precision)
{
return (Math.Abs(f1 - f2) <= precision);
}
public static bool AlmostEquals(this float f1, float f2)
{
return AlmostEquals(f1, f2, .00001f);
}
public static bool AlmostEquals(this float? f1, float? f2)
{
if (f1.HasValue && f2.HasValue)
{
return AlmostEquals(f1.Value, f2.Value);
}
else if (f1 == null && f2 == null)
{
return true;
}
return false;
}
}
6 ответов
поскольку кажется, что это связано с плавающей запятой, есть так много вещей, которые могут пойти не так. Видеть: C# - несогласованный результат математической операции на 32-разрядных и 64-разрядных и двойной точностью проблемы .Чистая
есть так много вещей, которые можно разбить с плавающими точками. И сравнение поплавков для равенства-это общее нет-нет. Вы должны проверить разницу, меньшую, чем разумный Эпсилон.
одна вещь, которая может вызвать поведение, которое вы видите, это ошибка, которая вызывает гонки. Присоединение отладчика может изменить время кода таким образом, что условие гонки больше не запускается.
чтобы исправить это, используйте надлежащей синхронизации, когда у вас есть несколько потоков, обращающихся к данным.
Я сравниваю некоторые значения float в методе IsEqual.
Это звучит как очень плохой идея. Вы не должны сравнивать поплавки для равенства, потому что вычисления с плавающей запятой не являются 100% точными, и вы можете получить ошибки представления и округления. Сравните, достаточно ли они близки друг к другу. Для расчетов, связанных с деньгами, вы, вероятно, хотите использовать тип.
вопросы, которые вы должны задать себе -
- мой код резьбовой? Разница во времени повлияет на выход
- кто-то вызывает Debug.Assert () с выражением, которое вызывает побочные эффекты?
- какие объекты реализуют IDisposable () и некоторые делают это таким образом, что изменяет состояние?
- вы P / вызываете в неуправляемый код?
номер 3-Очень вероятно, плохой мальчик в этом случае. Сбор мусора может быть очень разным в debug и release, и вы можете обнаружить, что при сборе мусора объект влияет на результат более позднего модульного теста.
и FYI, если вы используете NUnit и TestDriven.NET -два запуска тестов в разные заказы.
это часто бывает, так как сборка отладки не оптимизирована по умолчанию, и даже если вы включите ее, поведение при отладке очень отличается. Вы можете отключить "оптимизировать код" в настройках проекта для всех сборок на вкладке свойства->сборка.
есть, конечно, другие изменения, которые могут вызвать различия, например, Вы упомянули условные методы. Я обнаружил, что они редко являются причиной проблем, для меня это почти всегда оптимизатор.
Classic gotcha's оптимизатора включают методы, которые получают "встроенные", чтобы они не появлялись в стеке вызовов. Это вызывает проблемы при использовании системы.Диагностика.Классы StackFrame для определения текущей точки выполнения. Аналогично это повлияет на результат MethodBase.GetCurrentMethod или другие функции / поведение, которые зависят от метода выполнения.
тогда, конечно, есть много вещей, которые я видел, что оптимизатор делает, что я просто не могу объяснить вообще. Один такой пример был задокументирован и обсужден в посте'HashDerivedBytes-замена Rfc2898DeriveBytes, но почему?
Как предполагает Марк, это обычно является результатом проблемы, связанной с синхронизацией, часто условием гонки или проблемой синхронизации.
один из распространенных способов справиться с этой проблемой-использовать операторы "print" в затронутых областях, чтобы показать вам, что происходит. Если операторы печати (Console.WriteLine
, Response.Write
, logging или что-то еще) устраните проблему, сохраните значения в глобальных переменных и распечатайте глобалы после появления проблемы.
в последнее время это случилось со мной в коде, который считывался с последовательного порта. Действие отладки вызвало достаточное изменение времени, чтобы повлиять на то, как байты из последовательного порта были буферизованы, что изменило способ анализа буфера. Поскольку операторы печати изменили время, мне пришлось сохранить данные для вывода позже.
чтобы добавить мои два цента к этому, я недавно обнаружил, что у меня было сравнение дат в процедуре sql, вызванной тестированием. Все даты были автоматически сгенерированы ранее в процедуре тестирования, и значения были вставлены в БД, и поэтому иногда они были точно такими же (при использовании RunTests), в результате чего null возвращался в табличном соединении. Не то что я ожидал. Очевидно, что в режиме отладки, так как я медленно продвигаюсь через него, будет разница в автоматически сгенерированном время, которое означало, что я никогда не сталкивался с ошибкой. Я решил это, вставив
нарезание резьбы.Нитка.Сон(520)
там, где определенно будет задержка между действиями. Проблема решена.