2013-12-18 4 views
4

Мы знаем, что есть разница в размере A при определении A как это:памяти оптимизация выравнивание не только производительность, но и размер памяти

class A 
{ 
    short a; 
    double b; 
    short c; 
}; 

или как этот

class A 
{ 
    short a; 
    short c; 
    double b; 
}; 

Я предполагаю, что мы компилируем для 32-битной ОС, и мы сказали компилятору выровнять до 32 бит.

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

+0

Использовать '__attribute__ ((__packed __))' – Mine

+0

Это решение, которое я предполагаю? Так что нет таких проблем с размерами? по умолчанию? (Если нет, то почему? Если да , тогда спасибо :)!) – Narek

+2

Также рассмотрим последствия многомодульной компиляции. Если один модуль переупорядочил их, а другой не ... – Mysticial

ответ

4

Очень сложно. Для упорядочения полей в той же последовательности, что и определение структуры, требуется структура.

Это требование, вероятно, связано с тем, что Паскаль не имеет такого требования и вызывает неожиданные результаты.

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

+0

Я согласен с большинством ваших ответов, кроме «одного или двух циклов лишних»: некоторые процессоры просто не поддерживают не выровненные выборки, поэтому для доступа к смещенным полям необходимо иметь последовательности с несколькими инструкциями, если вы используете '__attribute __ ((__packed __)) ', например. Выравнивание происходит по уважительной причине. Кроме того, вы не можете надежно взять указатель на член структуры, если структура упакована на архитектуре, требующей выравнивания. –

+0

@JoeZ: Я очень хорошо помню 68000 за его раздражающую непривязанную ловушку памяти. К счастью, такие ограничения все чаще встречаются в современных процессорах. Даже 8086 не будет виноват в 16-битном доступе без выравнивания: это заняло немного больше времени. Если вы хотите выжать максимум из 5-мегагерцового педераста, вы должны выровнять слова. – wallyk

+0

Процессоры x86 обрабатываются не выровненными, конечно. Процессоры ARM не работают, если вы не включили соответствующий бит функции. (По крайней мере, это относится к ARMv7.) ​​Я считаю, что даже ошибки x86, если вы пытаетесь загрузить SSE-типы с неправильными границами выравнивания. –

1

не трудно для компилятора, запрещено стандартом (за одним исключением): Раздел 9.2.12:

Нестатические элементы данных а (несращения) класса объявлен без промежуточный спецификатор доступа выделяется таким образом, чтобы более поздние члены имели более высокие адреса в объекте класса. Порядок распределения нестатических элементов данных, разделенных доступ спецификатор не определен

исключения, члены с различным модификатором доступа могут быть reorderes так:

class A 
{ 
public: 
    int a; 
    int b; 
private: 
    int c; 
int d; 
{'\; 

а и б, не могут быть перераспределены , c и d не могут быть изменены, но (a и b) могут быть переупорядочены wit (c и d)

0

Компилятор не может изменять порядок из-за требований стандарта (грубо: уникальные, инкрементные адреса и для классов C++ сгруппированы с помощью модификатора доступа).

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

Однако иногда больший объем данных (элементов) означает более простые инструкции. Например, использование битовых полей для сбривания нескольких байтов означает гораздо более сложный код, существует компромисс между размером кода и размером данных; увеличение внутреннего цикла на 500 байт для сглаживания данных 2k может быть катастрофическим для оптимизации и локальности памяти кода.

Инструменты, такие как PVS Studio, могут выдавать предупреждение , когда порядок элементов структуры субоптимален и может быть улучшен.


[edit2] Что касается «Почему эти правила существуют»:

Tl; Др: это интересно, но не важно, это просто ради любопытства.

Первые (безжалостный копия другого SO ответа) соответствующие разделы стандарта:

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

Указатель на объект структуры стандартного макета, соответствующим образом преобразованный с использованием reinterpret_cast, указывает на его начальный член (или если этот элемент является битовым полем, а затем в единицу, в которой он находится) и наоборот. [Примечание. Таким образом, в рамках объекта структуры стандартного макета может быть указано неназванное заполнение, но не в его начале, по мере необходимости, для достижения соответствующего выравнивания. -end примечание]

(Обратите внимание, что это относится к C, C++ является немного более сложным.)

Стандарт мало говорит о причины, большинство из них "образованные догадок" здесь:

Отдельные адреса - это требование, вытекающее из других проблем стандарта.

Создание указателя на структуру, эквивалентное указателю на первый элемент, безусловно, связано с существующей практикой программирования. (Это позволяет «polymorphy данные» в C путем вложения «базовой-структуры» в качестве первого члена «производной структуры»)

сохраняющей порядок, как указано программистом:

The «ванильного предположение» было бы не уничтожать ручную оптимизацию для локализации данных. Включение элемента с наиболее доступным доступом сверху может улучшить локальность внутри структуры (что позволяет лучше кэшировать или сокращать инструкции адресации). Если компилятор переупорядочивает, эти оптимизации могут быть потеряны для меньшего прироста в размере общей структуры.

C++ требует группировку по спецификаторов доступа: (т.е. «все общественные VARS вместе, все защищенные вары вместе, все частные вары вместе): Я никогда не нашел оснований для этого (и я должен сказать, что это было немного удивительно). Я мог представить, что (возможно) целью было позволить реализациям компилятора использовать контроль доступа к аппаратным средствам («этот сегмент кода может не получить доступ к этой части памяти»). OTOH Я не знаю архитектуры, которая позволила бы на этом уровне Я не видел, чтобы спецификаторы доступа считались механизмом secuirty.

+0

Где я могу узнать больше о причинах, по которым существуют такие требования стандарта? – Narek

Смежные вопросы