LINUX.ORG.RU

Как выравнивать данные?


0

3

Современные процессоры не такие же, как те процессоры, на которых разрабатывали язык Си. В новых процессорах огромные кеши, которые состоят из «строк», и данные в них пересылаются целиковыми строками (это моя гипотеза), по крайней мере уж очищаются-то точно строками. «This enables several tricks, like making sure neighboring elements of an array never share the same cache line with each other (which may speed up certain kinds of concurrent code).»
Так или иначе, для данных сегодня очень полезно быть выровненными на границу 8 байт (при 64-х битной архитектуре вроде x86_64), или даже на границу 16-ти байт (стек вроде так просят выравнивать). «The choice of both Windows x64 and x86-64 System V to maintain 16-byte stack alignment». С мотивацией, что «некоторые типы данных, такие как SSE- и AVX-векторы, требуют выравнивания по границе 16 байт для корректной работы».

В спецификации языка Си возможность выравнивания типов на границу появилась начиная с C11.

«alignment of any given struct or union type is required by the ISO C standard to be at least a perfect multiple of the lowest common multiple of the alignments of all of the members of the struct or union in question.»

Мне надо, чтобы экземпляры структуры всегда выравнивались на границу 16 байт (в стеке и в куче). Как мне этого добиваться?

Пункт 7.15 стандарта C11:

7.15 Alignment <stdalign.h>
1 The header <stdalign.h> defines four macros.
2 The macro
alignas
expands to _Alignas; the macro
alignof
expands to _Alignof.
3 The remaining macros are suitable for use in #if preprocessing directives. They are
_ _alignas_is_defined
and
_ _alignof_is_defined
which both expand to the integer constant 1.

В C11, вы можете использовать ключевое слово alignas для установки выравнивания структуры.

alignas(16) struct MyStruct {
    // поля структуры
};

«Для выделения памяти для таких типов данных следует использовать функции aligned_alloc и подобные». И тут начинаются разброд и шатание:

  • C11

    void * aligned_alloc (size_t alignment, size_t size)

  • POSIX

    int posix_memalign (void **memptr, size_t alignment, size_t size)

В GCC, для выравнивания структур и объединений, можно использовать атрибут __attribute__((aligned(n))). Но непонятно, требование по выравниванию применятся к самой структуре в целом, или к каждому полю в ней (думал, что второе, а на самом деле оказалось, что первое).

To specify multiple attributes, separate them by commas within the double parentheses: for example, __attribute__ ((aligned (16), packed)). Ну ок, а в C11 как добится такого же эффекта (выравнивание + packed)?

В LLVM реализация стандарта C11 тоже должна быть.

Ещё есть пункт 7.19 с типом max_align_t

7.19 Common definitions <stddef.h>
1 The header <stddef.h> defines the following macros and declares the following types.
Some are also defined in other headers, as noted in their respective subclauses.
2 The types are
...
max_align_t
which is an object type whose alignment is as great as is supported by the implementation in all contexts;

Отсюда, насколько я понимаю, если мне надо выравнивание не менее чем на 16 байтов, использовать malloc в коде нельзя (только aligned_alloc, возможно с alignof).

Я это всё потому пишу, что наткнулся на github-е на фрагмент кода, и мне захотелось сделать по-аналогии, только выравнивать не на 4 байта, а на 16. Наличие четырёх битов позволило бы мне в них закодировать не только разницу в глубине/высоте поддеревьев, но и количество дочерних узлов (два, один, или ни одного). Зная, что в листе нет дочерних, можно выделять под него меньше памяти, а листов в дереве чуть ли не половина от всех узлов.

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

А что, если бы это был не Си, а какой-нибудь Rust?

«Один из способов управления выравниванием в Rust - использование атрибута #[repr(C)]. Этот атрибут гарантирует, что структура данных будет представлена в памяти так, как если бы она была написана на C. Это означает, что порядок и размер полей структуры будут соответствовать тому, что определено в C, и выравнивание будет соответствовать стандартам C»

«использование атрибута #[repr(align(16))] может привести к неэффективному использованию памяти, так как компилятор будет добавлять дополнительные заполнения в структуру, чтобы удовлетворить требование выравнивания.»

★★★★

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

Вы можете использовать любой тип для align (например массив фиксированной длины) чтобы задать любое выравнивание для s.

Чего?!

#include <stdio.h>
#include <stdint.h>

union u {
    struct {
        char data[11];
    } s;
    uint32_t align[32];
};

int main()
{
    printf("%d\n", (int) _Alignof(union u));
}

Печатает 4. У union выравнивание максимальное по его элементам, у массива из T выравнивание равно выравниванию T.

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

Печатает 4. У union выравнивание максимальное по его элементам, у массива из T выравнивание равно выравниванию T.

Хм, да, я ошибся. Тем не менее, мой основной посыл в том, что задавать выравнивание принудительно можно и средствами С 89-го года.

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

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

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

мой основной посыл в том, что задавать выравнивание принудительно можно и средствами С 89-го года.

Ну так это неправильный посыл, потому что нет нужной длинны типов в 89-м году.

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

дык если нет нужной длины типа - кто воспрепятствует задать структуру из нужного числа(с желаемым gcd с адресацией ) байт(char? али short unsigned) - и сделать одно из полей результирующего (под)объединения таковой

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

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

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

эээээ структура не всегда union

у структуры в общем случае есть некоторое число полей идущих(обычно) в порядке совпадающем с текстовым определение

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

так вот

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

соответственно

и у дИдов (в с89 - на самом деле всегда - прст по мере кодификации языка стандартами замещают вакханалию adhoc решений - чем-то что принимают за стандарт и рекомендуют(обязывают) к следованию)

были всегда механизмы получить тип нужного количества

struct name {
    char first[20];
    char last[20];
};

у переменной name - футпринт в памяти явно не меньше 40 знакомест

крч

…. без открыва от ….

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

божечки мой

размер типа не меньше сорока

о кратности(гранулярности) адреса по которому может располагаться тип

если вы называете размером типа кратность адреса по которому значения этого типа располагаются - успехов

вы правы.

qulinxao3 ★☆
()

что рекомендуют на isocpp.org ?

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

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

это всё прикольно

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

уже 2 заклоунили.

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

непонятен контекст

Он описан в стартовом сообщении. Хочу вместо указателей использовать индексы, битовые поля тоже вписать и всё это будет размером более 8-ми байтов.

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

непонятен контекст стартового сообщения

avl-деревья (как например пузырьковая сортировка) - не так универсальны в использовании как часты при ознакомительном обучении - их область оптимальности применимости менее широки - ибо существуют(стали известны в дальнейшем - да и были ещё до - но не так распространены были во времена оные)

указатели и есть индексы в памяти

– ваще возможнож нырнуть в сырцы компилятора и увидеть какие там ваще алигны возможны

если же нет пути - то урезать леща - пользоватся своим ali16_mallock - c потерей правильной сдвинутости для значений на стеке

инжинегрия это копромисы

qulinxao3 ★☆
()