Оптимальные опции для x86 GCC

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


    Какие опции в GCC по умолчанию?


          (1) По умолчанию в GCC используется уровень оптимизаций “-O0”. Он явно не оптимален с точки зрения производительности и не рекомендуется для компиляции конечного продукта.
    GCC не распознает архитектуру, на которой запускается компиляция, пока не передана опция ”-march=native”. По умолчанию GCC использует опцию, заданную при его конфигурации. Чтобы узнать конфигурацию GCC, достаточно запустить:
       gcc -v
       «Configured with: [PATH]/configure … --with-arch=corei7 --with-cpu=corei7…»

    Это означает что GCC добавит “-march=corei7” к вашим опциям (если не будет указана другая архитектура).
    Большинство GCC компиляторов для x86 (базовый для 64 битного Linux) добавляет: “-mtune=generic -march=x86-64” к заданным опциям, так как при конфигурации не были заданы опции, определяющие архитектуру. Вы всегда можете узнать все опции, передаваемые при запуске GCC, а также его внутренние опции при помощи команды:
       echo «int main {return 0;}» | gcc [OPTIONS] -x c -v -Q –

    В итоге, часто используемое:
       gcc -O2 test.c

    построит “test.c” без каких-либо специфичных архитектурных оптимизаций. Это может привести к существенному спаду производительности (относительно архитектурно оптимизированного кода). Отключенная или ограниченная векторизация и неоптимальное планирование кода являются наиболее распространенными причинами спада производительности, если не указать или указать неправильную архитектуру.
    Для указания текущей архитектуры надо компилировать так:
       gcc -O2 test.c -march=native

    Указание используемой архитектуры важно для производительности. Единственным исключением можно считать те программы, где вызов библиотечных функций занимает почти все время запуска. GLIBC может выбрать оптимальную для данной архитектуры функцию во время исполнения. Важно отметить, что при статической линковке некоторые GLIBC функции не имеют версий под различные архитектуры. То есть динамическая сборка лучше, если важна быстрота GLIBC функций..
          (2) По умолчанию большинство GCC компиляторов для x86 в 32 битном режиме используют x87 модель вычислений с плавающей точкой, так как были сконфигурированы без “-mfpmath=sse”. Только если GCC конфигурация содержит “--with-mfpmath=sse”:
       gcc -v
       «Configured with: [PATH]/configure … --with-mfpmath=sse…»

    компилятор будет использовать SSE модель по умолчанию.Во всех остальных случаях лучше добавлять опцию “-mfpmath=sse” к сборке в 32 битном режиме.
    Так, часто используемое:
       gcc -O2 -m32 test.c

    может привести к существенным потерям производительности в коде с вещественной арифметикой. Потому правильный вариант:
       gcc -O2 -m32 test.c -mfpmath=sse

    Добавление опции ”-mfpmath=sse” важно в 32 битном режиме! Исключением является компилятор, в конфигурации которого есть “--with-mfpmath=sse".



    32 битный режим или 64 битный?


          32 битный режим обычно используется для сокращения объема используемой памяти и как следствие ускорения работы с ней (больше данных помещается в кеш).
    В 64 битном режиме (по сравнению с 32 битным) количество доступных регистров общего пользования увеличивается с 6 до 14, XMM регистров с 8 до 16. Также все 64 битные архитектуры поддерживают SSE2 расширение, поэтому в 64 битном режиме не надо добавлять опцию “-mfpmath=sse”.
    Рекомендуется использовать 64 битный режим для счетных задач, а 32 битный режим для мобильных приложений.



    Как получить максимальную производительность?


          Определенного набора опций для получения максимальной проивзодительности не существует, однако в GCC есть много опций, которые стоит попробовать использовать. Ниже представлена таблица с рекомендуемыми опциями и прогнозами прироста для процессоров Intel Atom и 2nd Generation Intel Core i7 относительно опции “-O2”. Прогнозы основаны на среднем геометрическом результатов определенного набора задач, скомпилированных GCC версии 4.7. Также предполагается, что конфигурация компилятора была проведена для x86-64 generic.
         Прогноз увеличения производительности на мобильных приложениях относительно “-O2” (только в 32 битном режиме, так как он основной для мобильного сегмента):
    -m32 -mfpmath=sse ~5%
    -m32 -mfpmath=sse -Ofast -flto ~36%
    -m32 -mfpmath=sse -Ofast -flto -march=native ~40%
    -m32 -mfpmath=sse -Ofast -flto -march=native -funroll-loops ~43%

         Прогноз увеличения производительности на вычислительных задачах относительно “-O2” (в 32 битном режиме):
    -m32 -mfpmath=sse ~4%
    -m32 -mfpmath=sse -Ofast -flto ~21%
    -m32 -mfpmath=sse -Ofast -flto -march=native ~25%
    -m32 -mfpmath=sse -Ofast -flto -march=native -funroll-loops ~24%

         Прогноз увеличения производительности на вычислительных задачах относительно “-O2” (в 64 битном режиме):
    -m64 -Ofast -flto ~17%
    -m64 -Ofast -flto -march=native ~21%
    -m64 -Ofast -flto -march=native -funroll-loops ~22%

    Преимущество 64 битного режима над 32 битным для вычислительных задач при опциях “-O2 -mfpmath=sse” составляет около ~5%
    Все данные в статье являются прогнозом, основанном на результатах определенного набора бенчмарков.
    Ниже представлено описание используемых в статье опций. Полное описание (на английском): http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/Optimize-Options.html"
    • "-Ofast" аналогично "-O3 -ffast-math" включает более высокий уровень оптимизаций и более агрессивные оптимизации для арифметических вычислений (например, вещественную реассоциацию)
    • "-flto" межмодульные оптимизации
    • "-m32" 32 битный режим
    • "-mfpmath=sse" включает использование XMM регистров в вещественной арифметике (вместо вещественного стека в x87 режиме)
    • "-funroll-loops" включает развертывание циклов
    Intel
    237,00
    Компания
    Поделиться публикацией

    Комментарии 53

      +26
      gcc -O4 emails your code to Jeff Dean for a rewrite.

      А если серьезно: Intel рекламирует gcc? Я чего-то не понимаю?
        +2
        Вполне нормально. Достаточно много серъезных компаний использует gcc без обсуждений, но колеблется между AMD и Intel серверами.
        Потом делаются бенчмарки для AMD и Intel (gcc с соответствующими опциями для каждой архитектуры).
        Так что, это больше раскрутка Intel как архитектуры для выполнения кода, так как они понимают что не все могут просто так перелезть с gcc на icc или другие компиляторы.
          +1
          Разумно.

          Я уж было подумал, что бригады чертей прямо в этот момент носятся туда-сюда в поисках причин внезапного заморозка у них дома…
        +1
        "-flto" межмодульные оптимизации

        Вот про этот режим я бы поподробнее расписал как это работает. И как с этим флагом собрать webkit, если 12 Gb оперативной памяти оказывается мало?
          +3
          Никак. Но с ним можно собирать отдельные модули или использовать инкрементальную сборку. Хотя на практике мне это удалось использовать лишь один раз. Зато программа получилась в полтора раза компактнее и примерно на столько же быстрее. Но сколько было гемора…
            0
            Кстати, в новых версиях gcc всё стало попроще, и работает более менее сразу. Но большие проекты требуют излишне много памяти, хотя этому и есть простое объяснение.
              +1
              Можно смешивать объектные файлы с внутренним представлением и обычные. Таким образом Вы сможете применить lto только к нужной части проекта. Например, только там где недостает inline или только к горячим модулям.
                0
                А вот это интересно. Спасибо, не знал!
          –7
          Ждем ебилдов.
          –1
          А как в xcode скомпилировать код чтобы при этом _mh_execute_header.cpusubtype не выходил как CPU_SUBTYPE_386
          Мне вот давно уже хотелось бы компилить на CPU_SUBTYPE_PENTIUM_4
          только вот рецептами из вашей статьи воспользоваться не получилось
          при попытке установить «Other С Flags» как -march=native код ваще не компилится с ошибкой:
          Prefix.pch:1: error: bad value (native) for -march= switch
          Prefix.pch:1: Bad value (native) for -mtune= switch
            +2
            Там же clang.
              –2
              gcc 4.2 там тоже есть ;)
                0
                Он не умеет march=native. Фича только с gcc 4.3 появилась.
                  0
                  ясно. А может есть какой-либо другой way чтобы компилить для CPU_SUBTYPE_PENTIUM_4 на gcc 4.2?
                  0
                  Там gcc только как фронтенд к llvm. Настоящий gcc надо отдельно ставить.
              +2
              Однако:
              -O3: This is the highest level of optimization possible, and also the riskiest. It will take a longer time to compile your code with this option, and in fact it should not be used system-wide with gcc 4.x. The behavior of gcc has changed significantly since version 3.x. In 3.x, -O3 has been shown to lead to marginally faster execution times over -O2, but this is no longer the case with gcc 4.x. Compiling all your packages with -O3 will result in larger binaries that require more memory, and will significantly increase the odds of compilation failure or unexpected program behavior (including errors). The downsides outweigh the benefits; remember the principle of diminishing returns. Using -O3 is not recommended for gcc 4.x.

              Отсюда: www.gentoo.org/doc/en/gcc-optimization.xml
                0
                Сборка генты и сборка собственного приложения — никак не связанные вещи.
                  +4
                  Compiling all your packages your application with -O3 will result in larger binaries that require more memory, and will significantly increase the odds of compilation failure or unexpected program behavior (including errors).

                  Всё ещё актуально, по моему.
                    0
                    Однако, для своего приложения можно сделать тесты и при желании посмотреть ассемблерные листинги в критичных местах. Для all your packages — нет. Поэтому во многих числодробильных пакетах -O3/-Ofast есть во всех дистрибутивах, уже в оригинальном мейкфайле (не обязательно для всех файлов, естественно).
                      +1
                      -O3
                      Optimize yet more. -O3 turns on all optimizations specified by -O2 and also turns on the -finline-functions, -funswitch-loops, -fpredictive-commoning, -fgcse-after-reload, -ftree-vectorize and -fipa-cp-clone options.

                      Взято с gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/Optimize-Options.html

                      Согласен, что не так и много добавлено в сравнении с O2, но:
                      1. O3 включает векторизацию (в отдельных случаях это сильно разгоняет приложение).
                      2. Добавляет inline, что может и сократить код, если процедура объявлена static. Хотя чаще всего, конечно, увеличит как код, так и производительность.
                        0
                        O3 включает векторизацию (в отдельных случаях это сильно разгоняет приложение).

                        Недавно столкнулся с такой штукой — два вложенных цикла for:
                        for(i=0;i<x;i++) { for(k=0;k<16;k++) { s += a[k] + b[k] + a[k+1] + b[k+1];} a += y; b += y; }
                        O3 включает в себя развёртку циклов, которая до векторизации разворачивает внутренний цикл, после чего векторизатор не способен ничего сделать из-за кривого доступа к массиву. Без разворачивания внутренний цикл векторизуется, и на данном примере -O2 -ftree-vectorize ощутимо быстрее чем -O3
                          +1
                          O3 включает в себя развёртку циклов

                          В gcc на O3 включается более агрессивная развертка циклов с маленьким и статическим числом итераций, так называемый «full peeling». Это очень полезная оптимизация для тех архитектур, где предсказатель переходов не всегда оптимален и стоимость промаха велика (в основном это касается мобильных устройств).
                          векторизатор не способен ничего сделать из-за кривого доступа к массиву

                          Для 4.7 это действительно так. В 4.8 уже поправлено, и все векторизуется. Релиз уже очень скоро.
                          Как workaround могу посоветовать Вам опцию контроля peeling:
                          --param max-completely-peel-times=2
                          в сочетании с -funroll-loops она должна максимально разогнать приведенный цикл.
                            0
                            В 4.8 уже поправлено, и все векторизуется.

                            Буду рад если в релизе это так, я проверял на 4.8 от конца сентября 2012. В идеале хотелось бы, чтобы при полностью развернутом внутреннем цикле внешний векторизовался, тогда это позволит вынести редукцию по сложению совсем вне циклов.
                  +2
                  Было бы интересно увидеть сравнение по производительности icc т.е. насколько сами Intel могут приблизить gcc к icc :-)
                    +4
                    От набора тестов очень зависит. На спеках сравнение будет сильно не в пользу gcc.
                      +1
                      +1, пробовали как-то раз. На вычислительных бенчмарках gcc довольно сильно проигрывает, на большом коммерческом коде (в котором, впрочем, тоже очень много вычислений) уступает. Но это эксперименты двухгодичной давности, может быть, сейчас что-то изменилось.
                    0
                    А можно ли как-то систем-wide прописать настройку для ранее скомпилированного gcc без его пересборки?
                      0
                      export CFLAGS CXXFLAGS?
                        0
                        configure может и подхватит, а вот сам gcc нет:
                        datacompboy@nuuzerpogodible:~$ CFLAGS=-march=native echo "int main(){return 0;}" | gcc -x c -v -Q -
                        Using built-in specs.
                        COLLECT_GCC=gcc
                        COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.7/lto-wrapper
                        Target: x86_64-linux-gnu
                        Configured with: ../src/configure -v --with-pkgversion='Debian 4.7.2-4' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
                        Thread model: posix
                        gcc version 4.7.2 (Debian 4.7.2-4) 
                        COLLECT_GCC_OPTIONS='-v' '-Q' '-mtune=generic' '-march=x86-64'
                         /usr/lib/gcc/x86_64-linux-gnu/4.7/cc1 -v -imultiarch x86_64-linux-gnu - -dumpbase - -mtune=generic -march=x86-64 -auxbase - -version -o /tmp/ccoRhjPC.s
                        
                          +1
                          Можно попробовать сделать обертку:
                          1. переименовать gcc, g++ и gfortran например в *.4_7
                          2. сделать исполняемый скрипт в gcc (в остальных по аналогии):
                          gcc.4_7 $GCC_CFLAGS "$@"
                          Если gcc пускается с полным путем, то надо и полный путь к gcc.4_7 указать в скрипте.
                          3. А потом можно и export GCC_CFLAGS
                        –3
                        > Вы всегда можете узнать все опции, передаваемые при запуске GCC, а также его внутренние опции при помощи команды:
                        >
                        > echo "int main {return 0;}" | gcc [OPTIONS] -x c -v -Q -

                        Хм, у меня эта команда выдает вот что:

                        gcc: error: [OPTIONS]: Нет такого файла или каталога Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/i486-linux-gnu/4.7/lto-wrapper Target: i486-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Debian 4.7.1-7' --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.7 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --enable-targets=all --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu Thread model: posix gcc version 4.7.1 (Debian 4.7.1-7)

                        И где тут стандартные опции можно увидеть?
                          +1
                          echo "int main {return 0;}" | gcc -O1 -x c -v -Q -

                          echo "int main {return 0;}" | gcc -O2 -x c -v -Q -
                            +3
                            Под [OPTIONS] имелись ввиду Ваши опции, например, O2 или O3. Удобство в том, что упомянутая команда выводит:
                            1. Опции по дефолту (-march=, -mfpmath=)
                            2. Расшифровывает опции -O2, -O3, -Ofast и другие, которые могут отличаться для разных бранчей. GCC компиляторы в поставке операционных систем, тулов для разработки могут отличаться от основного и подключать разные оптимизации при тех же уровнях оптимизации.
                              –1
                              > Под [OPTIONS] имелись ввиду Ваши опции, например, O2 или O3.

                              Не понял, чтобы увидеть опции, надо написать опции???
                                0
                                Не понял, чтобы увидеть опции, надо написать опции???

                                Да. Указанная технология раскрывает внутренние опции компилятора. Например, "-O2" это комплекс оптимизаций, который может отличаться для разных бранчей.
                            0
                            LTO появился сравнительно недавно, до LTO я пользовался PGO (Profile-guided optimization)
                            habrahabr.ru/post/138132/
                            Эта штука СИЛЬНО ускоряет приложения и является (по моему мнению) необходимой в embedded-среде.
                              0
                              Как раз вчера задавался этим вопросом – с какими параметрами по умолчанию скомпилированны в rpm apache/mysql/php/apc и что из них можно выжать, перекомпилировав их с максимальными флагами с помощью gcc или тем более ICC (Intel, как вы могли в своём блоге упомянуть icc в сравнении с gcc? )

                              Здесь (http://vbtechsupport.com/715/ ) есть некий пример компиляции Mysql под Intel через gcc, но приведены совсем другие флаги ( –with-fast-mutexes, –with-aria-tmp-tables, –with-atomic-ops=smp –mtune=nocona. ) и совсем не упоминаются -Ofast -flto -march=native -funroll-loops. Кто более прав в этом случае?

                              И хотелось бы задать вопрос Intel: не хотите ли сделать свой репозитарий пакетов LAMP, сделанный через icc с максимальными оптимизациями под i7/xeon? Весь мир бы вам спасибо сказал за бесплатные свежие +5 — +20% производительности.
                                0
                                Intel, как вы могли в своём блоге упомянуть icc в сравнении с gcc?

                                Вроде не упоминали. Сатья про GCC только.
                                vbtechsupport.com/715/

                                В указанной статье речь идет о многопоточности. Здесь речь идет о последовательном коде.
                                –with-fast-mutexes, –with-aria-tmp-tables, –with-atomic-ops=smp

                                Скорее напоминает опции конфигурации и не противоречит -Ofast -flto -funroll-loops.
                                Но Вы правы, для каждого отдельного приложения оптимальный набор опций может быть свой.
                                И хотелось бы задать вопрос Intel

                                Ваш вопрос касается icc и лучше задать его в разделе Вопросы.
                                  0
                                  имелось в виду «не упомянуть»
                                    0
                                    Цель данной статьи помочь пользователям GCC на x86 ускорить свои приложения. Что касается сравнения GCC и ICC, то лучше чтобы это делали независимые люди.
                                  0
                                  И хотелось бы задать вопрос Intel: не хотите ли сделать свой репозитарий пакетов LAMP, сделанный через icc с максимальными оптимизациями под i7/xeon? Весь мир бы вам спасибо сказал за бесплатные свежие +5 — +20% производительности.


                                  Раз нет таких готовых сборок, значит похвастаться не чем.
                                  В LAMP нет какой-то математики навороченной, да и много там float/double? Видимо будет 5% прироста, а тогда все узнают что icc VC gcc это 5% и всё, маркетинг проиграет.
                                    0
                                    Речь ведь не только о float/double, но и обо всех длительных оптимизациях, о которых рассказано в статье. Не каждый ведь будет собирать с LTO и PDO по несколько часов на десятках гигабайт памяти, а так были бы готовые рабочие быстрые пакеты. Централизированный билд с максимально производительными настройками и автоматизированное тестирование упростило бы жизнь многим людям.
                                    Впрочем в том же mysql хватает целочисленной математики, подсчёт различных hash, b-tree и прочего, не зря есть даже экспериментальные проекты реляционных баз данных для GPGPU
                                  +1
                                  К сожалению, большинство описанной в статье информации относится не к GCC, а к компиляции для одной конкретной архитектуры х86. Особенно x87, SSE2 и так далее.
                                  А для мобильных приложений, которые многократно упомянуты не к месту, актуальна оптимизация для ARM, например включение инструкций ARMv7 и NEON.
                                    0
                                    А для мобильных приложений, которые многократно упомянуты не к месту

                                    Ну почему же не к месту? Вот есть примеры:
                                    habrahabr.ru/company/intel/blog/150099/
                                    habrahabr.ru/company/intel/blog/153183/

                                    Для ARM, наверное, есть свои оптимизации, но это тема для другой статьи.
                                    +1
                                    Думаю необходимо отметить, что агрессивные оптимизации типа ffast-math могут поменять результаты вычислений. И зачастую получается, что программа скомпилированная на разных платформах выдаёт разные цифры. Так что, например, в научных программах такие опции не рекомендуют употреблять.
                                      0
                                      Для всяких графических либ самое то.
                                      –2
                                      Сразу видно, что это перевод, причём очень плохой. Читать невозможно
                                        +1
                                        Не знаю что считать первоисточником (ибо автор один), но я действительно публиковал английский вариант статьи здесь:
                                        software.intel.com/en-us/blogs/2012/09/26/gcc-x86-performance-hints
                                        Так что это не совсем перевод. Просто очень многие сложившиеся технические термины тяжело сформулировать на русском языке.
                                          –4
                                          Эээ, то есть автор текста на обоих языках ты?

                                          Итак, текст запутан, воспринимается трудно.

                                          Вот например, (1): «По умолчанию GCC использует опцию, заданную при его конфигурации». Какая опция имеется в виду? "-march=native"? В таком случае возможны два варианта: либо такая опция есть, либо её нет. Тогда нужно написать так: «По умолчанию GCC использует опцию -march=native, если она была задана при конфигурации». А может быть, имелось в виду не это? Может имелась в виду опция -march=X, где вместо X могут быть разные параметры? А может быть, имелись в виду просто любые опции? То есть просто при конфигурации можно задать любые опции, которые будут добавляться к командной строке? Тогда нужно было написать «опции», а не «опция». В любом случае, из текста это непонятно.

                                          Скорее всего, имелось в виду -march=X. Хорошо, тогда так и надо было написать: «По умолчанию GCC использует тот параметр к опции -march, который указан при конфигурации». Во! Вот так намного понятнее.

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

                                          Текст скучный, сухой, неинтересный :)
                                        0
                                        К сожалению, -flto дает увеличение производительности не всегда, или мало.
                                        Больше эффекта можно получить от так называемого PGO (Profile-guided optimization), про него писали здесь.
                                        Для использования PGO необходимо собрать статистику использования кода, а затем компилятор сделает оптимизации под заданный кейс использования программы.
                                          0
                                          PGO это действительно сильный инструмент для поднятия производительности, однако, и более рисковый. Очень многое зависит от тренировочных данных. Подбор таких данных — сложная задача.
                                          PGO существенно замедляет сборку. LTO же позволяет распараллеливать сборку -flto=N и свести потери к минимуму, а иногда и ускориться.
                                          Собственно, никто не мешает использовать и то, и другое.
                                          Так что, если PGO ускоряет Ваш код и удобно в использовании, смело добавляйте его к -Ofast -flto -march=native (-funroll-loops по вкусу).
                                            0
                                            Спасибо за подсказку про распараллеливание сборки -flto=N (не всегда достаточно make -j N), очень долгая сборка у меня была одной из причин отказа от LTO, т.к. результата было мизер, а время сборки увеличилось в 2-3 раза.
                                            Для PGO я подаю «боевые» данные.

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

                                        Самое читаемое