GCC: программа не работает с опцией компиляции -O3 - PullRequest
8 голосов
/ 11 ноября 2008

Я пишу программу на C ++, которая не работает (возникает ошибка сегментации), когда я компилирую ее с оптимизацией (опции -O1, -O2, -O3 и т. Д.), Но она прекрасно работает, когда скомпилируйте его без оптимизации.

Есть ли вероятность того, что ошибка в моем коде? или я должен предположить, что это ошибка в GCC?

Моя версия GCC 3.4.6.

Есть ли какой-нибудь известный способ решения этой проблемы?

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


Это мой оригинальный функтор. Тот, который отлично работает без уровней оптимизации и вызывает ошибку сегментации с любым уровнем оптимизации:

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){
        return distance(point,p1) < distance(point,p2) ;
    }
} ;

И этот работает безупречно с любым уровнем оптимизации:

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){

        float d1=distance(point,p1) ;
        float d2=distance(point,p2) ;

        std::cout << "" ;  //without this line, I get a segmentation fault anyways

        return d1 < d2 ;
    }
} ;

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

Ответы [ 16 ]

8 голосов
/ 12 ноября 2008

Теперь, когда вы разместили фрагмент кода и был найден работающий обходной путь (ответ программиста Windows), я могу сказать, что, возможно, вы ищете -ffloat-store.

-ffloat-магазин

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

Эта опция предотвращает нежелательную избыточную точность на машинах, таких как 68000, где плавающие регистры (из 68881) сохраняют большую точность, чем предполагалось для двойной переменной. Аналогично для архитектуры x86. Для большинства программ избыточная точность приносит только пользу, но некоторые программы полагаются на точное определение плавающей запятой IEEE. Используйте -ffloat-store для таких программ, изменив их, чтобы сохранить все соответствующие промежуточные вычисления в переменных.

Источник: http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Optimize-Options.html

7 голосов
/ 11 ноября 2008

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

Ваш код компилируется с 0 предупреждениями?

 g++ -Wall -Wextra -pedantic -ansi
7 голосов
/ 11 ноября 2008

Вот код, который кажется работающим, пока вы не нажмете -O3 ...

#include <stdio.h>

int main()
{
    int i = 0, j = 1, k = 2;
    printf("%d %d %d\n", *(&j-1), *(&j), *(&j+1));
    return 0;
}

Без оптимизаций получаю «2 1 0»; с оптимизацией я получаю "40 1 2293680". Зачем? Потому что я и К оптимизированы!

Но я взял адрес j и вышел из области памяти, выделенной для j. Это не разрешено стандартом. Скорее всего, ваша проблема вызвана аналогичным отклонением от стандарта.

Я считаю, valgrind часто помогает в такие моменты.

РЕДАКТИРОВАТЬ: Некоторые комментаторы считают, что стандарт допускает произвольную арифметику указателей. Нет. Помните, что в некоторых архитектурах есть забавные схемы адресации, выравнивание может быть важным, и у вас могут возникнуть проблемы, если вы переполните определенные регистры!

Слова стандарта [черновика] о добавлении / вычитании целого числа из / из указателя (выделение добавлено):

"Если и операнд-указатель, и результат указывают на элементы одного и того же объекта массива или один после последнего элемента объекта массива, оценка не вызовет переполнения; в противном случае поведение не определено."

Видя, что & j даже не указывает на объект массива, & j-1 и & j + 1 вряд ли могут указывать на часть одного и того же объекта массива. Таким образом, простая оценка & j + 1 (не говоря уже о разыменовании) - это неопределенное поведение.

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

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

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

volatile float d1=distance(point,p1) ;
volatile float d2=distance(point,p2) ;
return d1 < d2 ;
5 голосов
/ 11 ноября 2008

Ошибка в вашем коде. Скорее всего, вы делаете что-то, что вызывает неопределенное поведение в соответствии со стандартом C, которое просто работает без оптимизации, но когда GCC делает определенные предположения для выполнения своих оптимизаций, код нарушается, когда эти предположения не верны. Обязательно скомпилируйте с параметром -Wall, и -Wextra также может быть хорошей идеей, и посмотрите, есть ли какие-либо предупреждения. Вы также можете попробовать -ansi или -pedantic, но это может привести к ложным срабатываниям.

4 голосов
/ 11 ноября 2008

Вы можете столкнуться с проблемой псевдонимов (или это может быть миллион других вещей). Найдите опцию -fstrict-aliasing.

На этот вопрос невозможно ответить правильно без дополнительной информации.

3 голосов
/ 11 ноября 2008

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

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

Кроме того, попробуйте разные версии GCC (компилировать свой собственный GCC очень легко, особенно в Linux). Если возможно, попробуйте с другим компилятором. В Intel C есть компилятор, более или менее совместимый с GCC (и, думаю, бесплатный для некоммерческого использования). Это поможет точно определить проблему.

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

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

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

Это почти ( почти ), никогда не компилятор.

Сначала убедитесь, что вы компилируете без предупреждения, с -Wall.

Если это не дало вам «эврики», подключите отладчик к наименее оптимизированной версии исполняемого файла, который вылетает, и посмотрите, что он делает и куда идет.

5 даст вам 10, что вы исправили проблему к этому моменту.

1 голос
/ 12 ноября 2008

Хорошо ... Это одна из самых странных проблем, с которыми я когда-либо сталкивался.
Я не думаю, что у меня достаточно доказательств, чтобы утверждать, что это ошибка GCC, но, честно говоря ... Она действительно похожа на одну.

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

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){
        return distance(point,p1) < distance(point,p2) ;
    }
} ;

И этот работает безупречно с любым уровнем оптимизации:

struct distanceToPointSort{
    indexedDocument* point ;
    distanceToPointSort(indexedDocument* p): point(p) {}
    bool operator() (indexedDocument* p1,indexedDocument* p2){

        float d1=distance(point,p1) ;
        float d2=distance(point,p2) ;

        std::cout << "" ;  //without this line, I get a segmentation fault anyways

        return d1 < d2 ;
    }
} ;

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

...