Время от времени код C ++ не будет работать при компиляции с некоторым уровнем оптимизации. Это может быть компилятор, выполняющий оптимизацию, которая нарушает код, или это может быть код, содержащий неопределенное поведение, которое позволяет компилятору делать все, что он чувствует.
Предположим, у меня есть фрагмент кода, который ломается, когда компилируется только с более высоким уровнем оптимизации. Как я узнаю, что это код или компилятор и что мне делать, если это компилятор?
optimization
compiler
Sharptooth
источник
источник
Ответы:
Я бы сказал, что можно с уверенностью сказать, что в подавляющем большинстве случаев это ваш код, а не компилятор, который не работает. И даже в экстраординарном случае, когда это компилятор, вы, вероятно, необычным образом используете какую-то непонятную функцию языка, для которой конкретный компилятор не подготовлен; другими словами, вы, скорее всего, могли бы изменить свой код на более идиоматический и избежать слабого места компилятора.
В любом случае, если вы можете доказать, что нашли ошибку компилятора (на основе спецификации языка), сообщите об этом разработчикам компилятора, чтобы они могли исправить ее через некоторое время.
источник
Как обычно, как и с любыми другими ошибками: выполните контролируемый эксперимент. Сузьте область подозрений, отключите оптимизацию для всего остального и начните изменять оптимизации, примененные к этому фрагменту кода. Как только вы получите 100% воспроизводимость, начните изменять свой код, вводя вещи, которые могут нарушить определенную оптимизацию (например, вводить возможный псевдоним указателя, вставлять внешние вызовы с потенциальными побочными эффектами и т. Д.). Также может помочь просмотр кода сборки в отладчике.
источник
Изучите полученный код сборки и посмотрите, выполняет ли он то, к чему призывает ваш источник. Помните, что шансы очень высоки, потому что это действительно ваш код, в чем-то неочевидный.
источник
За 30 лет программирования число найденных мной ошибок подлинного компилятора (генерации кода) все еще составляет ~ 10. Число моих собственных (и других) ошибок, которые я обнаружил и исправил за тот же период, вероятно, составляет > 10000 Моё «правило большого пальца» заключается в том, что вероятность того, что ошибка возникла из-за компилятора, составляет <0,001.
источник
Я начал писать комментарий, а потом решил, что это слишком долго и слишком точно.
Я бы сказал, что это ваш код не работает. В маловероятном случае, если вы обнаружили ошибку в компиляторе - вы должны сообщить об этом разработчикам компилятора, но на этом разница заканчивается.
Решение состоит в том, чтобы идентифицировать нарушающую конструкцию и реорганизовать ее так, чтобы она выполняла одну и ту же логику по-разному. Это, скорее всего, решит проблему, будь то ошибка на вашей стороне или в компиляторе.
источник
источник
int + int
переполнения точно так, как если бы он был скомпилирован в аппаратную инструкцию ADD. Он прекрасно работал при компиляции со старой версией GCC, но не при компиляции с новым компилятором. По-видимому, хорошие люди из GCC решили, что, поскольку результат целочисленного переполнения не определен, их оптимизатор может работать при условии, что этого никогда не произойдет. Это оптимизировало важную ветку прямо из кода.Если вы хотите знать, является ли это ваш код или компилятор, вы должны прекрасно знать спецификацию C ++.
Если сомнения сохраняются, вы должны отлично знать сборку x86.
Если у вас нет настроения учиться обоим до совершенства, то это почти наверняка неопределенное поведение, которое ваш компилятор решает по-разному в зависимости от уровня оптимизации.
источник
Получение ошибки компиляции в стандартном коде или внутренней ошибки компиляции более вероятно, чем ошибочные оптимизаторы. Но я слышал, что компиляторы оптимизируют циклы, неправильно забывая некоторые побочные эффекты, вызываемые методом.
У меня нет предложений о том, как узнать, если это вы или компилятор. Вы можете попробовать другой компилятор.
Однажды мне стало интересно, был ли это мой код или нет, и кто-то предложил мне valgrind. Я потратил 5 или 10 минут, чтобы запустить свою программу с ним (я думаю,
valgrind --leak-check=yes myprog arg1 arg2
сделал это, но я играл с другими вариантами), и она сразу показала мне ОДНУ линию, которая проходила под одним конкретным случаем, который был проблемой. Тогда мое приложение работало без ошибок, без каких-либо странных сбоев, ошибок или странного поведения. valgrind или другой подобный инструмент - это хороший способ узнать, является ли он вашим кодом.Примечание: однажды я удивился, почему производительность моего приложения отстой. Оказалось, что все мои проблемы с производительностью были в одной строке. Я написал
for(int i=0; i<strlen(sz); ++i) {
. СЗ было несколько мб. По какой-то причине компилятор запускается каждый раз, даже после оптимизации. Одна строка может иметь большое значение. От выступлений до сбоевисточник
Все более распространенная ситуация заключается в том, что компиляторы нарушают код, написанный для диалектов C, который поддерживает поведение, не предписанное Стандартом, и позволяет кодам, нацеленным на эти диалекты, быть более эффективными, чем может быть строго согласованный код. В таком случае было бы несправедливо описывать как «сломанный» код, который был бы на 100% надежным для компиляторов, которые реализовали целевой диалект, или описывать как «сломанный» компилятор, который обрабатывает диалект, который не поддерживает требуемую семантику. , Вместо этого проблемы проистекают просто из того факта, что язык, обрабатываемый современными компиляторами с включенной оптимизацией, отличается от диалектов, которые раньше были популярны (и до сих пор обрабатываются многими компиляторами с отключенными оптимизациями или некоторыми даже с включенными оптимизациями).
Например, много кода написано для диалектов, которые распознают как законный ряд шаблонов наложения указателей, не предписанных интерпретацией стандарта gcc, и используют такие шаблоны, чтобы сделать прямой перевод кода более читабельным и эффективным чем было бы возможно при интерпретации GCC стандарта C. Такой код может быть несовместим с gcc, но это не значит, что он не работает. Он просто полагается на расширения, которые gcc поддерживает только с отключенными оптимизациями.
источник
Изолируйте проблемное место и сравните наблюдаемое поведение с тем, что должно происходить в соответствии со спецификацией языка. Определенно нелегко, но это то, что вы должны сделать, чтобы знать (а не просто предполагать ).
Я, вероятно, не был бы таким дотошным. Скорее, я бы попросил форум поддержки / список рассылки производителя компилятора. Если это действительно ошибка в компиляторе, то они могут это исправить. Вероятно, это был бы мой код в любом случае. Например, языковые спецификации, касающиеся видимости памяти при многопоточности, могут быть довольно противоречивыми, и они могут стать очевидными только при использовании определенных флагов оптимизации на некотором конкретном оборудовании (!). Некоторое поведение может быть не определено спецификацией, поэтому оно может работать с некоторым компилятором / некоторыми флагами, а не работать с некоторыми другими и т. Д.
источник
Скорее всего, ваш код имеет неопределенное поведение (как объяснили другие, у вас гораздо больше шансов иметь ошибки в коде, чем в компиляторе, даже если компиляторы C ++ настолько сложны, что у них есть ошибки; даже в спецификации C ++ есть ошибки проектирования) , И UB может быть здесь, даже если скомпилированный исполняемый файл работает (к счастью).
Так что вам следует прочитать блог Латтнера « Что должен знать каждый программист на C» о неопределенном поведении (в основном это относится и к C ++ 11).
Инструмент valgrind и последние
-fsanitize=
опции инструментовки для GCC (или Clang / LLVM ) также должны быть полезны. И, конечно же, включите все предупреждения:g++ -Wall -Wextra
источник