В моем коде определено много массивов данных, длина которых указана только в инициализаторе массива.
int a[] = {1, 2, 3, 4};
int b[] = {1, 2, 3, 4, 5};
Для обеспечения согласованности строк кэша я хочу дополнить их до размера строки кэша (32). Следующий код можно использовать без уточнения точной информации о размере.
struct {
int payload[4];
} __attribute__((aligned(32))) a = {
.payload = {1, 2, 3, 4}
};
struct {
int payload[5];
} __attribute__((aligned(32))) b = {
.payload = {1, 2, 3, 4, 5}
};
// sizeof(a.payload) == 4 * sizeof(int), sizeof(b.payload) == 5 * sizeof(int)
Однако таких данных довольно много, вручную переписать их по отдельности сложно. Есть ли способ определить макрос для обработки этого, например
#define ARRAY_INITIALIZER (array, initializer) ...
#define ARRAY_PAYLOAD(array) array.payload
ARRAY_INITIALIZER(a, {1, 2, 3, 4});
ARRAY_INITIALIZER(b, {1, 2, 3, 4, 5});
Самая трудность здесь заключается в том, как вычислить длину инициализатора, не могу найти для этого никакого указания, может ли кто-нибудь здесь помочь?
Можно ли применить __attribute__((aligned(32)))
к массиву как одну переменную, а не как поле структуры?
Зачем помещать массивы в структуру? Ваш реальный вопрос, как выровнять исходные 32-байтовые массивы?
@nielsen, размещение внутри структуры имеет 2 эффекта: 1) заполнение конечных данных, не только выравнивание по началу, но и выравнивание по концу 2) sizeof
все равно можно получить фактический размер без конечного заполнения
@StasSimonov, aligned
to переменная приведет к выравниванию только по началу, скажем, массив из 5 начинается с выравнивания по 32, левые 27 байтов все еще доступны для выделения другим переменным, мы хотим, чтобы он занимал полные 32 байта, чтобы избежать проблем с когерентностью кеша
@SupportUkraine, вы правы, я внесу изменения
__attribute__
— это GNU-изм. Некоторые другие реализации C поддерживают его для совместимости с GCC, но это не стандарт C.
Но GNU C позволяет применять __attribute__((aligned))
как к переменным, так и к типам, поэтому я не совсем понимаю, почему вы рассматриваете возможность обертывания массивов в структуры только для выравнивания.
@JohnBollinger, __attribute__((aligned))
к переменной выравнивает только ее начало. При применении его к структуре он выравнивает начало и конец. См. Ссылка1 и Ссылка2.
Эта инициализация создает выровненные адреса либо на C, либо на C++:
#include <stdalign.h>
alignas(32) int a[] = {1, 2, 3, 4} ;
alignas(32) int b[] = {1, 2, 3, 4, 5};
sizeof(b)
равно 20, что не выровнено по 32 байтам, что оставляет возможность того, что b
и другие переменные находятся в одной строке кэша, что может вызвать потенциальные проблемы.
Вот одна из идей использования вариативных макросов:
#include <stdalign.h> // for "alignas" - not needed since C23
#define VA_COUNT(...) (sizeof((int[]){0, __VA_ARGS__}) / sizeof(int) - 1)
#define ARRAY_INITIALIZER(V, ...) \
struct { \
alignas(32) int payload[VA_COUNT(__VA_ARGS__)]; \
} V = {.payload = {__VA_ARGS__}}
Обратите внимание, что стандарт _Alignas
(alignas
) используется для выравнивания массива.
Размер полезной нагрузки рассчитывается с помощью VA_COUNT
.
Пример использования:
#include <stdio.h>
// the macro definitions from above goes here
#define SIZE(x) (sizeof(x) / sizeof *(x))
int main(void) {
ARRAY_INITIALIZER(a, 1, 2, 3, 4);
for (unsigned i = 0; i < SIZE(a.payload); ++i) {
printf("%d\n", a.payload[i]);
}
}
Выход:
1
2
3
4
Привет, Тед. В моей среде нет alignas
, поэтому я все еще использую__attribute__((aligned(32)))
после структуры. Я также меняю {0, __VA_ARGS__}
на __VA_ARGS__
, потому что хочу, чтобы _VA_ARGS содержал полный инициализатор {...}
, и результат был в порядке. Есть ли причина, по которой вы добавляете к этому дополнительный 0? Я проверяю sizeof((int[]){})
и все компилируется нормально
Это может быть полезно: stackoverflow.com/questions/2124339/… Да, это C++, но определение макроса может подойти и вам.