LINUX.ORG.RU

Неверное выравнивание

 ,


3

3

Добрый день. Столкнулся с непонятной проблемой - пример всё поясняет:

#include <iostream>
struct NN
{
    double dd;
    char i;
};

int main()
{
    NN ar[2];
    std::cout << "alignof double(в норме 8) == " << alignof(double) << '\n';
    std::cout << "alignof NN(в норме 8) == " << alignof(NN) << '\n';
    std::cout << "sizeof NN(в норме 16) == " << sizeof(NN) << '\n';
    std::cout << "array difference(в норме 16) == " << (int)&ar[1] - (int)&ar[0] << std::endl;
    return 0;
}

$ g++ --version
g++ (Debian 4.9.2-16) 4.9.2

$ ./a.out
alignof double(в норме 8) == 8
alignof NN(в норме 8) == 4
sizeof NN(в норме 16) == 12
array difference(в норме 16) == 12
По-моему, компилятор нездоров. Проверял пример на онлайн компиляторах, такой ошибки не обнаружил. gcc ставил с репозитория. Что об этом думаете? В чём причина?

★★

alignof NN(в норме 8) == 4

Вот этого я объяснить не могу.

sizeof NN(в норме 16) == 12

А вот это вполне понятно: double = 8 байт, char = 1 байт, char выровнян на 4 байта, поэтому между ними вставлено ещё 3.

такой ошибки не обнаружил

А можно цитату из C11 про правила выравнивания структур? А то я не уверен, что это запрещённое поведение.

proud_anon ★★★★★
()
Ответ на: комментарий от proud_anon

А вот это вполне понятно: double = 8 байт, char = 1 байт, >>char выровнян на 4 байта, поэтому между ними вставлено ещё >>3.

Давайте порассуждаем:

$ getconf LEVEL1_DCACHE_LINESIZE
64
Размер кэш линии 64 байта. Представим, что у нас масси из NN. Пять элементов укладываются в кэш линию полностью (12*5=60), далее идёт double шестого элемента, который ложит 4 байта в одну кэш линию, а 4 байта в следущую. Поэтому мой ответ - выравнивание 12 байт это неверно, адрес должен быть кратен выравниванию элемента (12 не кратно 8).

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

адрес должен быть кратен выравниванию элемента (12 не кратно 8)

Не адрес, а размер, если только ты не хотел написать тавтологию. Выравнивание NN, как тебе сообщил gcc, почему-то равно 4. 12 кратно 4.

Сейчас я проверил у себя:

% g++ --version
g++ (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

% g++ -std=c++0x -fpermissive -march=i686 -m32 -mtune=generic -O0 -o testalign testalign.cc

% ./testalign
alignof double(в норме 8) == 8
alignof NN(в норме 8) == 4
sizeof NN(в норме 16) == 12
array difference(в норме 16) == 12

% g++ -std=c++0x -fpermissive -march=x86-64 -m64 -mtune=generic -O0 -o testalign testalign.cc
testalign.cc: In function ‘int main()’:
testalign.cc:17:72: warning: cast from ‘NN*’ to ‘int’ loses precision [-fpermissive]
     std::cout << "array difference(в норме 16) == " << (int)&ar[1] - (int)&ar[0] << std::endl;
                                                                        ^
testalign.cc:17:86: warning: cast from ‘NN*’ to ‘int’ loses precision [-fpermissive]
     std::cout << "array difference(в норме 16) == " << (int)&ar[1] - (int)&ar[0] << std::endl;

% ./testalign
alignof double(в норме 8) == 8
alignof NN(в норме 8) == 8
sizeof NN(в норме 16) == 16
array difference(в норме 16) == 16

proud_anon ★★★★★
()
Последнее исправление: proud_anon (всего исправлений: 3)
Ответ на: комментарий от proud_anon

Поэтому мой ответ - выравнивание 12 байт это неверно, адрес >>должен быть кратен выравниванию элемента (12 не кратно 8).

Хотел написать это:

Поэтому мой ответ - размер 12 байт это неверно, адрес >>должен быть кратен выравниванию элемента (12 не кратно 8).

Т.е. такой размер приведёт к тому, что адрес адрес double перестанет быть кратным выравниванию, в результате мы положим double в две кэш линии (в случаи массива).

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

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Но поведение компилятора яснее для меня не стало.

       -malign-double
       -mno-align-double
           Control whether GCC aligns "double", "long double", and "long long"
           variables on a two-word boundary or a one-word boundary.  Aligning
           "double" variables on a two-word boundary produces code that runs
           somewhat faster on a Pentium at the expense of more memory.

           On x86-64, -malign-double is enabled by default.

           Warning: if you use the -malign-double switch, structures
           containing the above types are aligned differently than the
           published application binary interface specifications for the 386
           and are not binary compatible with structures in code compiled
           without that switch.
Sorcerer ★★★★★
()
Ответ на: комментарий от Sorcerer

Спасибо, перекомпилил тест с -malign-double, всё стало как положенно.

Но gcc не прав, когда возвращает alignof(double) == 8 (без -malign-double) для x86.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

gcc прав в первом случае у тебя выравнивание в один дабл, меньше чем сам дабл он быть не может, значит = 8 второй случай структура + чар,где чар 4 байта, выравнивание структуры идет по меньшему, а значит по чару который 4 байта ну итд

алинг-дабл, это варавнивание по наименьшему взятому как дабл

anonymous
()
Ответ на: комментарий от anonymous

О чём вы? Выравнивание структуры == выравнивание члена с самым строгим (большим) выравниванием.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

В случае массива это проблема выравнивания массива, а не структуры-элемента массива. Поля самой структуры же выравнены вполне логично.

unC0Rr ★★★★★
()
Ответ на: комментарий от pavlick

Хочешь фокус покажу?

% cat testalignc.c
#include <stdio.h>
#include <stdalign.h>

int main() {
	printf("alignof(double) == %u\n", alignof(double));
	return 0;
}

% gcc -std=c11 -m32 -march=i686 -o testalignc testalignc.c

% ./testalignc                                            
alignof(double) == 4

proud_anon ★★★★★
()
Ответ на: комментарий от anonymous

Выравнивание double не обязательно 8, не отрицаю (варьирует в зависимости от архитектуры, просто привык что 8 и alignof(double) вернула 8). Но разве alignof(double) не должен возвращать правильное значение?

pavlick ★★
() автор топика
Ответ на: комментарий от unC0Rr

Нет, это проблема правильной организации элемента массива. Вы где-то видели массивы с заполнителями между элементами?

pavlick ★★
() автор топика
Ответ на: комментарий от proud_anon

я запустил на x64 и у меня тоже выдало
alignof double(в норме 8) == 8
alignof NN(в норме 8) == 8
sizeof NN(в норме 16) == 16
array difference(в норме 16) == 16
почему по ссылкам сходил и не почитал ? зачем флудишь и заставляешь тебе разжевывать?

anonymous
()
Ответ на: комментарий от anonymous

почему по ссылкам сходил и не почитал ? зачем флудишь и заставляешь тебе разжевывать?

Давай прежде чем давать советы, ты прочитаешь этот тред?

я запустил на x64 и у меня тоже выдало

Давай ты прочитаешь вот этот комментарий: Неверное выравнивание (комментарий)

И объяснишь, почему на x86 (32-битной) в режиме C++ (но не чистого C) alignof(double) == 8, но alignof(NN) == 4.

proud_anon ★★★★★
()
Ответ на: комментарий от anonymous

Что за тон? Лучше сам сходи в начало темы. Ты думаешь я вывод сочинил? Это выдала программа. Я считаю вывод невалидным (при таком alignof(double)). По твоим ссылкам ходил, почитал.

В принципе, вопросов больше нет. Спасибо участникам.

pavlick ★★
() автор топика
Ответ на: комментарий от proud_anon

иди по ссылкам и читай почему твоя структура не должна выравниваться на 8, а выравнялась на 4, как и должно быть на x86

anonymous
()
Ответ на: комментарий от pavlick

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

proud_anon ★★★★★
()

ТС, ты не должен делать так:

struct NN
{
    double dd;
    char i;
};

ты должен делать так:

struct NN
{
    boost::endian::big_int32_t dd;
    boost::endian::big_int8_t i;
};

или так:

struct NN
{
    boost::endian::little_int32_t dd;
    boost::endian::little_int8_t i;
};

http://www.boost.org/doc/libs/1_58_0/libs/endian/doc/index.html тебе в помощь.

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 1)
Ответ на: комментарий от anonymous

8 второй случай структура + чар,где чар 4 байта,

Это у кого чар 4 байта?

выравнивание структуры идет по меньшему, а значит по чару который 4 байта ну итд

Это в каком стандарте структуру выравнивают по наименьшему полю?

andreyu ★★★★★
()
Последнее исправление: andreyu (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.