Каковы некоторые причины, по которым сборка Release будет работать иначе, чем сборка Debug

У меня есть программа Visual Studio 2005 С++, которая работает в режиме Release по-разному, чем в режиме Debug. В режиме выпуска происходит (кажущаяся) прерывистая авария. В режиме отладки он не падает. Каковы некоторые причины, по которым сборка Release будет работать иначе, чем сборка Debug?

Также стоит упомянуть, что моя программа довольно сложна и использует несколько сторонних библиотек для обработки XML, брокерских сообщений и т.д.

Спасибо заранее!

Ответ 1

Выжить версии версии дает хороший обзор.

Вещи, с которыми я столкнулся - большинство из них уже упоминалось

Переменная инициализация безусловно, самый распространенный. В Visual Studio отладочные сборки явно инициализируют выделенную память для заданных значений, см., Например, Значения памяти здесь. Эти значения, как правило, легко обнаружить, вызывают ошибку за пределами границ при использовании в качестве индекса или нарушения доступа при использовании в качестве указателя. Однако неинициализированное логическое значение истинно и может вызывать неинициализированные ошибки памяти, которые не обнаружены в течение многих лет.

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

Изначальная инициализация исходной памяти также может быть различной в сборке релизов, независимо от того, начинаете ли вы с визуальной студии (прикрепленный отладчик) и начиная с проводника. Это делает "самый приятный" вид ошибок сборки, которые никогда не появляются под отладчиком.

Действительные оптимизации - это второе место в моем опыте. Стандарт С++ позволяет проводить множество оптимизаций, что может быть неожиданным, но вполне справедливым, например. когда два указателя имеют одинаковое расположение в памяти, порядок инициализации не рассматривается или несколько потоков изменяют одни и те же ячейки памяти, и вы ожидаете определенного порядка, в котором поток B видит изменения, сделанные потоком A. Часто компилятор обвиняется в эти. Не так быстро, молодые йеди! - см. ниже

Сроки Выпускные сборки не просто "работают быстрее" по разным причинам (оптимизация, функции ведения журнала, обеспечивающие точку синхронизации потока, код отладки, такой как утверждения не выполняются и т.д.), а также относительный время между операциями резко меняется. Наиболее распространенной проблемой, которая обнаруживается в этом случае, являются условия гонки, а также взаимоблокировки и простой "разный порядок" выполнения кода сообщения/таймера/события. Несмотря на то, что это проблемы с синхронизацией, они могут быть на удивление стабильными между сборками и платформами с репродукциями, которые "работают всегда, кроме ПК 23".

Байт защиты. Отладочные сборки часто ставят (более) защитные байты вокруг выбранных экземпляров и распределений, чтобы защитить от переполнения индекса, а иногда и от переполнения. В редких случаях, когда код использует смещения или размеры, например. сериализуя необработанные структуры, они разные.

Другие отличия кода Некоторые инструкции - например, утверждают - ничего не оценивают в сборках релизов. Иногда у них разные побочные эффекты. Это распространено с макро-обманом, как в классическом (предупреждение: несколько ошибок)

#ifdef DEBUG
#define Log(x) cout << #x << x << "\n";
#else 
#define Log(x)
#endif

if (foo)
  Log(x)
if (bar)
  Run();

Что в сборке релизов оценивается как if (foo & bar) Этот тип ошибки очень редок с обычным кодом C/С++ и макросами, которые правильно написаны.

Ошибки компилятора Это никогда не случается. Ну, это так, но вы большей частью своей карьеры предпочитаете, полагая, что это не так. За десять лет работы с VC6 я нашел тот, где я все еще убежден, что это ошибка незафиксированного компилятора, по сравнению с десятками шаблонов (возможно, даже сотен экземпляров) с недостаточным пониманием Священного Писания (стандарт a.k.a.).

Ответ 2

В отладочной версии часто используются утверждения и/или символы отладки. Это может привести к разному расположению памяти. В случае плохой указатель, переполнение массива или аналогичной памяти вы получаете доступ в одном случае с критической плохой памятью (например, указатель на функцию), а в другом случае может быть только некоторая некритическая память (например, только строка документа выгружается)

Ответ 3

Переменные, которые не инициализируются явно, будут или не будут обнулены в сборке Release.

Ответ 4

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

Ответ 5

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

На некоторых языках или при использовании многих разных библиотек это может привести к прерывистым сбоям - особенно когда выбранный уровень оптимизации очень высок.

Я знаю, что это имеет место с компилятором gcc С++, но я не уверен в компиляторе Microsoft.

Ответ 6

У меня была аналогичная проблема не так давно, которая в конечном итоге вызвана тем, что стек обрабатывается по-разному в сборках релизов. Другие вещи, которые могут различаться:

  • Распределение памяти обрабатывается по-разному с помощью отладочных сборников в компиляторе VS (то есть, записывая 0xcc поверх очищенной памяти и т.д.).
  • Развертывание цикла и другие оптимизации компилятора
  • Выбор указателей

Ответ 7

Это зависит как от поставщика компилятора, так и от библиотек, которые вы компилируете с помощью флагов DEBUG. Хотя код DEBUG никогда не должен влиять на исполняемый код (он не должен иметь побочных эффектов), он иногда делает.

В частности, переменные могут быть инициализированы только в режиме DEBUG и оставлены неинициализированными в режиме RELEASE. STL в компиляторах Visual Studio отличается в режимах DEBUG и RELEASE. Идея состоит в том, что итераторы полностью проверяются в DEBUG для обнаружения возможных ошибок (используя недействительные итераторы, например, итератор в вектор недействителен, если вставка происходит после получения итератора).

То же самое происходит с сторонними библиотеками, первое, что я могу представить, это QT4, который завершит вашу программу утверждением, если поток, отличный от того, который создал графический объект, выполняет операции рисования.

При всех изменениях ваш код и память будут отличаться в обоих режимах. Указатель (чтение позиции, проходящей через конец массива) может быть не обнаружен, если эта позиция читаема.

Утверждения предназначены для того, чтобы убить приложение во время DEBUG и исчезнуть из сборки RELEASE, поэтому я не буду думать о утверждениях как о вашей проблеме. Моим первым подозреваемым был бы указатель изгоев или доступ к нему за один конец.

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

Ответ 8

http://www.debuginfo.com/tips/userbpntdll.html

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

Используйте PageHeap (или, если у вас установлены инструменты отладки, вы можете использовать gflags), чтобы обнаружить ошибки, связанные с поврежденными кучами.

http://support.microsoft.com/?id=286470

Ответ 9

По моему опыту, наиболее распространенная причина заключается в том, что конфигурации различаются в большей степени, чем настройки release/сборки. Например. различные библиотеки включены, или сборка отладки имеет другой размер стека, чем сборка выпуска.

Чтобы избежать этого в наших проектах Visual Studio 2005, мы широко используем листы свойств. Таким образом, конфигурации выпуска и отладки могут иметь общие настройки.

Ответ 10

Этот пост вместе с предоставленными ссылками очень помогает в исправлении связанной ошибки. добавление к вышеуказанному списку, разница в соглашениях о вызове также может привести к такому поведению. Это не удалось в сборке релизов только с оптимизацией для меня. i объявлен как __stdcall и определен как __cdecl (по умолчанию). [странно это предупреждение не выбрано даже на уровне предупреждения 4 MSVC?]

Ответ 11

Среди оптимизаций, которые могут быть выполнены в режиме выпуска (а не в отладке), разрешение копирования может давать разные результаты. В частности, RVO (оптимизация возвращаемого значения), в зависимости от того, как спроектированы ваши конструкторы.

Что такое оптимизация копирования и возврата значений?