По каким причинам сборка Release будет работать иначе, чем сборка Debug - PullRequest
55 голосов
/ 23 ноября 2008

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

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

Заранее спасибо!

Ответы [ 10 ]

122 голосов
/ 23 ноября 2008

Выжившая версия выпуска дает хороший обзор.

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

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

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

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

Действительные оптимизации на втором месте в моем опыте. Стандарт C ++ позволяет проводить много оптимизаций, что может быть удивительно, но вполне допустимо, например. когда два указателя имеют псевдоним в одной и той же ячейке памяти, порядок инициализации не учитывается или несколько потоков изменяют одну и ту же ячейку памяти, и вы ожидаете определенного порядка, в котором поток 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 / C ++ и правильно написанных макросах.

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

6 голосов
/ 23 ноября 2008

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

5 голосов
/ 23 ноября 2008

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

2 голосов
/ 23 ноября 2008

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

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

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

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

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

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

2 голосов
/ 23 ноября 2008

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

  • Распределение памяти обрабатывается по-разному в отладочных сборках в компиляторе VS (т. Е. При записи 0xcc поверх очищенной памяти и т.
  • Развертывание цикла и другие оптимизации компилятора
  • Подсветка указателей
2 голосов
/ 23 ноября 2008

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

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

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

2 голосов
/ 23 ноября 2008

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

1 голос
/ 12 мая 2009

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

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

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

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

0 голосов
/ 17 мая 2009

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

0 голосов
/ 10 мая 2009

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

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

...