Перейти к содержанию
    

ARM Compiler 6 (LLVM/CLang): можно ли компилировать несколько файлов как один?

2 hours ago, Arlleex said:

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

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

раз уж решили проверить лично, если не сложно, поделитесь потом на сколько лучше стало в байтах/тактах 

по сравнению с/без -flto

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 hours ago, _pv said:

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

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

но много ли таких проектов и у кого? к слову, у меня ни одного нет и не припомню чтобы были такие

 

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

Это сказывается лишь на времени работы над проектом. 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Впервые столкнулся с таким приёмом, просматривая исходники какого-то опенсурсного проекта. Проект использовал sqlite, собранную в один файл.

Приём называется Amalgamation. Здесь информация применительно к sqlite, пишут, увеличивает производительность на 5...10% и рекомендуют использовать только так. У них же можно подсмотреть работу с исходниками.

Изменено пользователем vov4ick

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

28 minutes ago, vov4ick said:

Впервые столкнулся с таким приёмом, просматривая исходники какого-то опенсурсного проекта.

такая схема может оказаться полезной для жирных сторонних библиотек, например попробую заценить это на открытой LUA библиотеке, а почему бы нет )))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

23 часа назад, Arlleex сказал:

кто-нибудь так сам делает, или это я только велосипеды изобретаю?))

Был такой компилятор "языка, похожего на C" для AVR - CodeVision, очень любимый теми, кого сейчас называют "ардуинщиками" (ардуину тогда еще не придумали), он внутри себя работал именно так - сваливал в кучу все исходные файлы и компилил их как один большой.

20 часов назад, Arlleex сказал:

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

LTO (link-time optimization) делает это из коробки для всего проекта.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, Сергей Борщ сказал:

LTO (link-time optimization) делает это из коробки для всего проекта.

Это да, но с LTO иногда бывают некие особенности, особенно когда активно описываются размещения объектов программы.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, Сергей Борщ сказал:

LTO (link-time optimization) делает это из коробки для всего проекта.

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

 

PS: Если кто-то думает что оптимизация - это пара процентов выигрыша, то советую посмотреть на работу оптимизирующих компиляторов для DSP, для хорошо написанного и спроектированного кода. Там выигрыш от оптимизации бывает даже не кратный, а бывает ускорение выполнения даже в десятки раз.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 hours ago, jcxz said:

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

PS: Если кто-то думает что оптимизация - это пара процентов выигрыша, то советую посмотреть на работу оптимизирующих компиляторов для DSP, для хорошо написанного и спроектированного кода. Там выигрыш от оптимизации бывает даже не кратный, а бывает ускорение выполнения даже в десятки раз.

Как имея в этом же юните компиляции код других функций, как правило не особо связанных (они ведь изначально в разных файлах не просто так оказались) можно ими увеличить быстродействие чего-то?

Экономия инструкции call на встраивании функци, или знание что какие-то аргументы константные (-flto это поди учитывает), это будут те самые упомянутные 5%, ценой размера.

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

 

Запихивание всё в один файл с #include "scr1.c" #include "scr2.c" #include "scr3.c" ..., разве что с системой сборки может помочь,

gcc all.c и всё, а не эти ваши дурацкие мэйкфайлы :blum3:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, _pv сказал:

Экономия инструкции call на встраивании функци, или знание что какие-то аргументы константные

Мелко мыслите.

Встраивание даёт не только экономию на call/возврат. Это как раз ерунда. Самое главное что оно даёт: увеличение количества доступных регистров в вызывающей функции. Соглашения вызова для ARM-кода говорят, что R0-R3,R12 могут быть разрушены вызываемой функцией. Поэтому вызывающая функция не может использовать эти регистры для хранения своих переменных. Даже если вызываемой функции требуется только один R0. Аналогично для регистров R4-R11,LR: вызываемая функция должна сохранить их если использует. Даже если в вызывающей функции эти регистры не используются. Получается - куча лишних команд сохранения/восстановления на стек. Кроме того - часто бывает, что вызывающая и вызываемая функции используют одни и те же константы (например - адреса какой-то памяти или периферии). Соответственно: 1) не нужно их повторно грузить в регистры; 2) уменьшение количества требуемых регистров для их хранения.

И не обязательно даже для встраиваемой функции. Те же самые оптимизации компилятор может использовать для обычной вызываемой функции, если она описана в данном .c-файле и описана как static.

 

Но это всё мелочи по сравнению с более профитными случаями оптимизации. Например есть код:

1. Читаем из ОЗУ некоторые данные; по ним проводим сложные, тяжёлые вычисления одной или нескольких переменных.

2. Вызываем некую функцию f1().

3. Снова читаем из ОЗУ те же данные что в п.1 и делаем те же вычисления.

Если бы не вызов f1(), то, очевидно, компилятор смог бы выкинуть шаг 3, использовав данные предыдущих вычислений. Но он ведь не знает, что именно делает f1()? Может она внутри меняет содержимое той ОЗУ, которая используется для вычислений п.1 и п.3?

Так вот - если f1() находится в другом, отдельном элементе компиляции, то компилятор не имеет права выкинуть вычисления п.3 (использовав предыдущие данные из п.1). Но, если он будет знать, что она например вообще не обращается к ОЗУ (или будет знать, что обращается, но - к памяти не перекрывающейся с ОЗУ, используемой в п.1 и п.3), то он точно также сможет оптимизировать п.3. А ведь там могут быть реально тяжёлые вычисления. Вот уже и ускорение почти двукратное! А если таких участков много?

 

И не только такое может быть. Есть и другие приёмы, используемые оптимизирующими компиляторами. Я выше писал уже.

1 час назад, _pv сказал:

Оптимизации DSP всё-таки немного про другое

Оптимизации DSP это не обязательно "другое". Читаем описанный выше случай (п.1...п.3). Так вот - например "Code Composer Studio" для DSP имеет квалификаторы указателей, позволяющие программисту указать, что используемые в коде указатели не имеют "side effects" (т.е. - читаемые/записываемые алгоритмом участки ОЗУ не перекрываются). А значит компилятор может поступать с операциями по этим указателям гораздо свободнее.

К сожалению - IAR for ARM не имеет подобных квалификаторов. Хотя и для ARM они были бы полезны (менее конечно, чем для DSP, в силу специфики архитектуры).  :sad:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, jcxz сказал:

Мелко мыслите.

Я быстро не не нашел четкого описания, какие именно оптимизации делает LTO в gcc. Но найденное описание как именно он их делает показывает, что все описанное вами он, теоретически, делать может. Так что кто именно "мелко мыслит" - вопрос пока спорный.

1 час назад, jcxz сказал:

К сожалению - IAR for ARM не имеет подобных квалификаторов.

Очень жаль (на самом деле - мне пофиг, gcc их имеет). Квалификатор restrict внесен еще в стандарт c99.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, jcxz said:

Мелко мыслите.

возможно, но имхо лишнее сохранение регистров в прологе функций та же экономия на спичках что и c call/ret.

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

если уж sqlite'у это настолько сильно помогло, для мелких проектов в МК если специально не писать код так чтобы там было что оптимизировать таким образом разница будет ещё меньше.

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

А если что-то "не влазит" по скорости на эти 5%, то менять там надо явно не оптимизацию сборки, особенно таким образом.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, _pv сказал:

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

Кто бы говорил.  :sarcastic:

Сами же себе противоречите в другой теме.... Читаем:

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 minutes ago, jcxz said:

Сами же себе противоречите в другой теме

где там в этих трёх строчках хоть один вызов функции? 🙂

20 minutes ago, jcxz said:

кратное ускорение получится

вот бы дисассемблер посмотреть...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

34 минуты назад, _pv сказал:

где там в этих трёх строчках хоть один вызов функции? 🙂

Если те строчки оформить в виде функции. Или как иначе ещё они должны исполняться без функции?

34 минуты назад, _pv сказал:

вот бы дисассемблер посмотреть...

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

4 часа назад, Сергей Борщ сказал:

Я быстро не не нашел четкого описания, какие именно оптимизации делает LTO в gcc. Но найденное описание как именно он их делает показывает, что все описанное вами он, теоретически, делать может. Так что кто именно "мелко мыслит" - вопрос пока спорный.

В описанном мной конкретном варианте кода 

Компилятор создаст код, в котором будет: куча команд для п.1; затем - вызов f1(); затем - куча команд для п.3 (почти таких же как для п.1). В случае же оптимизации (с инлайнингом f1(0 или если просто компилятор знает, что она не меняет память, используемую в п.1, п.3), будет только первая куча команд, а второй или вообще не будет или она будет небольшой. Т.е. - два варианта совершенно разного результирующего кода для приведённого алгоритма.

Как именно здесь поможет LTO? Ведь для создания другого кода (набора команд) нужна компиляция (другого кода), а не компоновка готового кода из имеющихся кусков.

Приведите конкретный пример - как именно LTO сможет создать такой код?

4 часа назад, Сергей Борщ сказал:

(на самом деле - мне пофиг, gcc их имеет).

Я рад за Вас.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

28 минут назад, jcxz сказал:

Как именно здесь поможет LTO?

Пишите тестовый пример, проверим и будем говорить опираясь на факты.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гость
Эта тема закрыта для публикации ответов.
×
×
  • Создать...