Вопрос по optimization, gcc, struct, c – Почему GCC не оптимизирует структуры?

42

Системы требуют, чтобы определенные примитивы были выровнены с определенными точками в памяти (от кратного числа байтов, кратного 4, коротких до байтов, кратных 2, и т. Д.). Конечно, они могут быть оптимизированы, чтобы тратить наименьшее количество места при заполнении.

У меня вопрос, почему GCC не делает это автоматически? Отсутствует ли более очевидная эвристика (переменные порядка от требования к наибольшему размеру до наименьшего)? Является ли некоторый код зависимым от физического упорядочения его структур (это хорошая идея)?

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

Вы можете попробовать-fipa-struct-reorg опция в структуре-реорг-ветке.Is there a GCC keyword to allow structure-reordering? phuclv

Ваш Ответ

7   ответов
1

Компиляторы C не упаковывают автоматическиprecisely because вопросов выравнивания, как вы упоминаете. Доступ не на границах слов (32-разрядные на большинстве процессоров) влечет за собой серьезные потери на x86 и приводит к фатальным ловушкам на архитектурах RISC.

Я не говорил о том, чтобы избавиться от буферизации, я говорил о том, чтобы поставить все длинные / указатели сквозными, затем все шорты сквозными, затем все символы сквозными и т.д. так что вы теряете место только в конце. Alex Gartrell
Ой. Да, это вызывает проблемы с двоичными форматами, как засвидетельствовал Коди. Кроме того, ANSI гарантирует, что смещения элементов структуры должны быть в порядке возрастания.
Вы не потеряете ни одного из преимуществ заполнения, правильно организовав структуру. С коротким, char, char, вы можете иметь заполнение 0, но все элементы попадают на правильное смещение. В общем, вы не потеряете в этом никакой скорости, так как они падают на свои естественные границы. Alex Gartrell
@ Алекс, ошибайся. Вы собираетесь тратить столько же места, так как ваш персонаж должен быть дополнен тем же количеством. Вы не получите никакой выгоды ни в пространстве, ни в производительности.
Ну, это наполовину правда. Компилятор C по умолчанию упаковывает их, они просто делают это в соответствии с естественными границами слова архитектуры. Вот почему вам нужно использовать структуры #pragma pack (0), использующие символы / шорты в упакованных протоколах, чтобы не допустить добавления отступов.
0

Возможно, вы захотите попробовать последнюю версию gcc trunk или struct-reorg-branch, которая находится в стадии активной разработки.

https://gcc.gnu.org/wiki/cauldron2015?action=AttachFile&do=view&target=Olga+Golovanevsky_+Memory+Layout+Optimizations+of+Structures+and+Objects.pdf

9

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

Чтение / запись структур непосредственно в файл в любом случае не переносимы компилятором / платформой из-за выравнивания (что разрешено), см.this  ТАК ответь.
1

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

Я считаю, что ядро Linux делает это для связанных списков.
74

gcc не переупорядочивает элементы структуры, потому что это нарушит стандарт Си. Раздел 6.7.2.1 стандарта C99 гласит:

Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared.

Хотя это нарушит Стандарт, существует полезный метод переупорядочения для защиты ядра Linux от руткитов / эксплойтов: часть Linux KSPP (kernsec.org/wiki/index.php/Kernel_Self_Protection_Project) - это рандомизация / переупорядочение некоторых структурных полей:openwall.com/lists/kernel-hardening/2017/05/26/8 (Представьте плагин рандомизации структуры структуры), соответствующий документ:sec.taylor.edu/doc/…  («Улучшенная безопасность ядра за счет рандомизации структуры памяти» - Д.М. Стэнли - & # x200E; 2013)
Да, но почему это было определено таким образом?
@ nes1983 Я видел код числового интегрирования, который предполагает, что все его входы являются числами с плавающей запятой в последовательном порядке. Вы передаете ему указатель на первое значение для интегрирования и последнее, и он сканирует между ними. Однако вы храните информацию в структуре, потому что для всего, кроме интеграции, это более удобный формат.
@ nes1983 Программист может делать предположения о порядке данных в структуре и может использовать маскировку для получения каждой части. Если структура переупорядочена, маскировка может быть неверной.
@ Evo510: я в замешательстве. Чтобы использовать маскирование, вы также должны знать отступы, что не гарантируется языком. Таким образом, вы не можете использовать маски. Я что-то пропустил?
25

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

это не имеет ничего общего с сетевыми или файловыми структурами. Действительно, заголовок структуры BMP тесно упакован с элементами, попадающими на неприродные границы, которые чужды компилятору.
@ JohannesSchaub-litb, что зависит; если оба компилятора придерживаются одного и того же ABI, у них нет причин создавать несовместимый код. Примерами являются GCC и Clang, а также 32-разрядные GCC и MSVC для C в Windows.
Ваше первое замечание очень верно. но я думаю, что ваш второй не т. скомпилированный код из разных компиляторов не совместим в любом случае.
Да? Вы неправильно поняли вопрос. Перечитайте второй абзац, где он говорит о структуре упорядочения. Это полностью отличается от дополнения.
6

У gcc SVN есть оптимизация реорганизации структуры (-fipa-struct-reorg), но она требует анализа всей программы и в настоящее время не очень эффективна.

Ну, это наполовину правда. Компилятор C по умолчанию упаковывает их, они просто делают это в соответствии с естественными границами слова архитектуры. Вот почему вам нужно использовать структуры #pragma pack (0), использующие символы / шорты в упакованных протоколах, чтобы не допустить добавления отступов.
Вы не потеряете ни одного из преимуществ заполнения, правильно организовав структуру. С коротким, char, char, вы можете иметь заполнение 0, но все элементы попадают на правильное смещение. В общем, вы не потеряете в этом никакой скорости, так как они падают на свои естественные границы. Alex Gartrell
Stock gcc 10 лет спустя (версия 7.2, упакованная Ubuntu 17.10) не документирует эту опцию на странице руководства. Как ни странно, строка параметров распознается исполняемым файлом gcc.
Я считаю, что ядро Linux делает это для связанных списков.

Похожие вопросы