Как выделить 16-байтовые данные с выравниванием по памяти
Я пытаюсь реализовать векторизация SSE на куске кода, для которого мне нужен мой 1D-массив, который будет выравниваться по 16 байт. Тем не менее, я попробовал несколько способов выделить 16-битные данные с выравниванием по памяти, но в итоге он выровнен на 4 байта.
Мне нужно работать с компилятором Intel icc.
Это пример кода, который я тестирую с помощью:
#include <stdio.h>
#include <stdlib.h>
void error(char *str)
{
printf("Error:%s\n",str);
exit(-1);
}
int main()
{
int i;
//float *A=NULL;
float *A = (float*) memalign(16,20*sizeof(float));
//align
// if (posix_memalign((void **)&A, 16, 20*sizeof(void*)) != 0)
// error("Cannot align");
for(i = 0; i < 20; i++)
printf("&A[%d] = %p\n",i,&A[i]);
free(A);
return 0;
}
Это результат, который я получаю:
&A[0] = 0x11fe010
&A[1] = 0x11fe014
&A[2] = 0x11fe018
&A[3] = 0x11fe01c
&A[4] = 0x11fe020
&A[5] = 0x11fe024
&A[6] = 0x11fe028
&A[7] = 0x11fe02c
&A[8] = 0x11fe030
&A[9] = 0x11fe034
&A[10] = 0x11fe038
&A[11] = 0x11fe03c
&A[12] = 0x11fe040
&A[13] = 0x11fe044
&A[14] = 0x11fe048
&A[15] = 0x11fe04c
&A[16] = 0x11fe050
&A[17] = 0x11fe054
&A[18] = 0x11fe058
&A[19] = 0x11fe05c
Он каждый раз выравнивается по 4 байта, я использовал как memalign, так и posix memalign. Поскольку я работаю над Linux, я не могу использовать _mm_malloc, и я не могу использовать _aligned_malloc.
Я получаю ошибку повреждения памяти, когда пытаюсь использовать _aligned_attribute (что подходит для gcc, я думаю).
Может ли кто-нибудь помочь мне точно генерировать 16-байтовые данные с выравниванием памяти для icc на платформе linux.
Ответы
Ответ 1
Выделяемая память выравнивается по 16 байт. См:
&A[0] = 0x11fe010
Но в массиве float
каждый элемент имеет 4 байта, а второй - по 4 байтам.
Вы можете использовать массив структур, каждый из которых содержит один float, с атрибутом aligned
:
struct x {
float y;
} __attribute__((aligned(16)));
struct x *A = memalign(...);
Ответ 2
Адрес, возвращаемый функцией memalign
, равен 0x11fe010
, который кратен 0x10
. Таким образом, функция делает правильные вещи. Это также означает, что ваш массив правильно выровнен по 16-байтовой границе. То, что вы делаете позже, это печать адреса каждого следующего элемента типа float
в вашем массиве. Поскольку размер float
составляет ровно 4 байта в вашем случае, каждый следующий адрес будет равен предыдущему +4. Например, 0x11fe010 + 0x4 = 0x11FE014
. Конечно, адрес 0x11FE014
не кратен 0x10
. Если вы должны выровнять все поплавки на границе по 16 байт, вам придется тратить 16 / 4 - 1
байтов на элемент. Дважды проверьте требования к используемым функциям.
Ответ 3
AFAIK, как memalign
, так и posix_memalign
выполняют свою работу.
&A[0] = 0x11fe010
Это соответствует 16 байтам.
&A[1] = 0x11fe014
Когда вы выполняете &A[1]
, вы сообщаете компилятору добавить одну позицию в указатель float
. Это неизбежно приведет к:
&A[0] + sizeof( float ) = 0x11fe010 + 4 = 0x11fe014
Если вы намерены иметь каждый элемент внутри вашего вектора, выровненный по 16 байт, вам следует рассмотреть объявление массива структур шириной в 16 байт.
struct float_16byte
{
float data;
float padding[ 3 ];
}
A[ ELEMENT_COUNT ];
Затем вы должны выделить память для ELEMENT_COUNT
(20, в вашем примере) переменных:
struct float_16byte *A = ( struct float_16byte * )memalign( 16, ELEMENT_COUNT * sizeof( struct float_16byte ) );
Ответ 4
Я нашел этот код на Wikipedia:
Example: get a 12bit aligned 4KBytes buffer with malloc()
// unaligned pointer to large area
void *up=malloc((1<<13)-1);
// well aligned pointer to 4KBytes
void *ap=aligntonext(up,12);
where aligntonext() is meant as:
move p to the right until next well aligned address if
not correct already. A possible implementation is
// PSEUDOCODE assumes uint32_t p,bits; for readability
// --- not typesafe, not side-effect safe
#define alignto(p,bits) (p>>bits<<bits)
#define aligntonext(p,bits) alignto((p+(1<<bits)-1),bits)
Ответ 5
Я лично считаю, что ваш код верен и подходит для кода Intel SSE. Когда вы загружаете данные в регистр XMM, я считаю, что процессор может загружать только 4 смежных поплавковых данных из основной памяти с первым, выровненным на 16 байт.
Короче говоря, я считаю, что вы сделали именно то, что хотите.