2011-01-24 4 views
8

У нас есть некоторые модульные тесты, которые не работают при запуске в режиме Release vs debug mode. Если я присоединяю отладчик в режиме деблокирования, тесты проходят. Для публикации здесь слишком много кода, поэтому я действительно ищу лучшие методы в отладке проблем режима выпуска. Я проверил для:Код ведет себя по-разному в режиме Release vs Debug

  • Директивы препроцессора DEBUG и RELEASE, но я не нашел их.
  • Conditional Methods

РЕШЕНИЕ: В этом случае, потому что я сравнения переменных с плавающей точкой на равенство. Я не мог изменить поплавки в десятичной системе без главного рефакторинга, поэтому я добавил метод расширения:

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; 
    } 
} 
+1

Пара вопросов. 1. Какие неудачи вы получаете, чтобы задать вопрос «вкус»? 2. Вы проверили условные методы? –

+0

Основная проблема заключается в том, что метод Equals возвращает false. Однако, если я принимаю каждое утверждение отдельно, все они верны. Если я попытаюсь подключить отладчик, проблема исчезнет. –

+0

Связано ли это с плавающей точкой (тип данных double и т. Д.)? – stefan

ответ

3

Поскольку это связано с плавающей точкой, существует так много вещей, которые могут пойти не так. См: C# - Inconsistent math operation result on 32-bit and 64-bit и Double precision problems on .NET

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

+0

См. Также http://stackoverflow.com/questions/2342396/why-does-this-floating-point-calculation-give-different-results-on-different-mach/2343351#2343351 –

8

Одна вещь, которая может вызвать поведение, которое вы видите, это ошибка, которая вызывает race condition. Прикрепление отладчика может изменить время кода, так что условие гонки больше не срабатывает.

Чтобы исправить это, используйте синхронизацию соответствующим образом, когда у вас есть несколько потоков, обращающихся к данным.


Я сравниваю несколько значений с плавающей точкой в ​​методе IsEqual.

Это звучит как очень плохая идея. Вы не должны сравнивать float для равенства, потому что вычисления с плавающей запятой не точны на 100%, и вы можете получить ошибки представления и округления. Сравните, чтобы увидеть, достаточно ли они близко друг к другу. Для расчетов с использованием денег вы, скорее всего, захотите использовать тип decimal.

+0

Спасибо за ваш ответ, однако эта часть приложения однопоточная. –

+0

Это должны быть числа с плавающей запятой. Я сделаю некоторые изменения и подтвержу. –

0

Как указывает Марк, обычно это связано с проблемой, связанной с синхронизацией, часто состоянием гонки или проблемой синхронизации.

Одним из распространенных способов решения этой проблемы является использование выражений «print» в затронутых областях, чтобы показать вам, что происходит. Если операторы печати (Console.WriteLine, Response.Write, протоколирование или что-то еще) устраняют проблему, сохраняйте значения в глобальных переменных и печатайте глобальные значения после появления проблемы.

Самое последнее, что случилось со мной, было в коде, который читался из последовательного порта. Отладочная активность вызвала только изменение времени, чтобы повлиять на то, как буферизируются байты из последовательного порта, что изменило процесс обработки буфера. Поскольку операторы печати изменили время, я должен был сохранить данные до вывода позже.

2

Вопросы вы должны спросить себя -

  1. резьбе мой код? Разница во времени будет влиять на выход
  2. Кто-нибудь вызывает Debug.Assert() с выражением этого побочного эффекта?
  3. Какие объекты реализуют IDisposable(), а некоторые делают это так, чтобы изменить состояние?
  4. Вы используете P/Invoking в неуправляемом коде?

Номер 3 является очень вероятным плохим парнем в этом случае.Сбор от мусора может отличаться при отладке и выпуске, и вы можете обнаружить, что когда объект собирает мусор, он влияет на результат более позднего модульного теста.

И FYI, если вы используете NUnit и TestDriven.NET - два тестовых теста в разных заказах.

+0

+1 для обозначения различий в сборе мусора. В режиме отладки объекты, как правило, остаются в живых дольше (например, до конца метода), чтобы поддерживать отладку, а в режиме выпуска объекты arre часто собираются раньше. – stakx

2

Это часто бывает так, что по умолчанию функция debug не оптимизирована, и даже если вы ее активируете, поведение при отладке сильно отличается. Вы можете отключить «Оптимизировать код» из параметров проекта для всех сборок на вкладке «Свойства ->».

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

Классический метод оптимизации включает методы, которые получают «встроенный», чтобы они не отображались в стеке вызовов. Это вызывает проблемы при использовании классов System.Diagnostics.StackFrame для определения текущей точки выполнения. Точно так же это повлияет на результат метода MethodBase.GetCurrentMethod или других функций/поведения, которые полагаются на исполняемый метод.

Тогда, конечно, есть много вещей, которые я видел, как оптимизатор делает то, что я просто не могу объяснить вообще. Один такой пример был задокументирован и обсужден в сообщении «HashDerivedBytes - replacing Rfc2898DeriveBytes, but why?», но я никогда не решал эту тайну. Я знаю только, что оптимизатор просто плоский сломал Rfc2898DeriveBytes, когда он используется для генерации ряда производных байтов. Как ни странно, это только сломалось, когда генерируемые байты не были равномерно делятся на размер используемого алгоритма хэша (20) и только произвели неправильные результаты после первых 20 байтов.

Дело в том, что оптимизация, отрицательно влияющая на код, не является новой составляющей для компиляторов. Большинство разработчиков C++ старой школы скажут вам, что сразу, а затем, как и я, зайдите в длинную зарисованную историю о том, как они работали вокруг нее;)

+1

Так что если сборка с оптимизацией ON запускается под отладчиком, будет ли какая-то оптимизация подавлена, что может привести к поведению, описанному в исходном вопросе? Примером этого может быть очень полезно. – Govert

0

Просто, чтобы добавить мои два цента к этому, я недавно обнаружил, что у меня было сравнение даты в процедуре sql, которую вызвало тестирование. Даты были автоматически сгенерированы ранее в тестовой процедуре, и значения были вставлены в БД, и поэтому иногда они были точно такими же (при использовании RunTests), заставляя нуль возвращаться при объединении таблиц. Не то, что я ожидал. Очевидно, что в режиме отладки, поскольку я медленно продвигаюсь по нему, будет разница в автоматически генерируемых временах, что означает, что я никогда не сталкивался с ошибкой. Я решил, вставив

Threading.Thread.Sleep (520)

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