Как выделить 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.


person PGOnTheGo    schedule 18.06.2012    source источник
comment
Откуда вы знаете, что он выровнен по 4 байтам, просто потому, что printf выводит только 4 байта за раз? Просто потому, что вы используете подпрограмму memalign, вы помещаете ее в тип float. Когда вы печатаете с помощью printf, он знает, как обрабатывать его примитивный тип (с плавающей запятой).   -  person trumpetlicks    schedule 18.06.2012
comment
Почему вы не можете использовать _mm_malloc в Linux?   -  person Z boson    schedule 28.10.2013


Ответы (6)


Выделенная память выровнена по 16 байтам. См.:
&A[0] = 0x11fe010
Но в массиве float каждый элемент имеет размер 4 байта, поэтому второй элемент выровнен по 4 байтам.

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

struct x {
    float y;
} __attribute__((aligned(16)));
struct x *A = memalign(...);
person ugoren    schedule 18.06.2012
comment
Я думаю, что __attribute__ - это встроенная функция GCC, недоступная для ICC. - person Benoit; 18.06.2012
comment
@Benoit, GCC действительно специфичен, но я думаю, что ICC его поддерживает. см. здесь - person ugoren; 18.06.2012
comment
@Benoit: Если вам нужно выровнять структуру по 16, просто добавьте 12 байтов заполнения в конце... - person ; 18.06.2012
comment
@VladLazarenko, работает, но не красиво и не портативно. Если в каком-то компиляторе изменить float, у вас снова будет плохое выравнивание. - person ugoren; 18.06.2012
comment
@ugoren: по этой причине вы можете добавить статическое утверждение, отключить заполнение для структуры и т. д. __attribute__((aligned(16))) на самом деле не работает с gcc и не является переносимым. - person ; 18.06.2012
comment
@VladLazarenko, атрибут не работает с GCC?! Возможно, вы имели в виду, что работает только с GCC. На самом деле это не переносимо, но компилятор сообщит вам, когда он не работает. Утверждение является альтернативой. В общем, я всегда предпочитаю сообщать компилятору, что я хочу, как можно точнее, и в этом случае я хочу выравнивание, а не заполнение (я все равно получу заполнение, но это не то, что я хочу). - person ugoren; 18.06.2012
comment
@ugoren: Нет, я имею в виду, что в некоторых случаях это не работает с GCC. т.е. если он выделен в стеке или выравнивание меньше минимальных требований к выравниванию и т. д. Кроме того, для этого атрибута все еще есть открытые ошибки. Это так же надежно, как добавление отступов самостоятельно. - person ; 18.06.2012
comment
В стеке ничто не может привести к выравниванию выделения за пределы выравнивания стека (обычно 8 байт). Но он выделяет динамически. Я не понимаю, как минимальное необходимое выравнивание имеет значение. И я много использовал этот атрибут с GCC 3 и 4 и никогда не встречал такой ошибки. - person ugoren; 18.06.2012

Адрес, возвращаемый функцией memalign, равен 0x11fe010, что кратно 0x10. Так что функция работает правильно. Это также означает, что ваш массив выровнен правильно по 16-байтовой границе. Что вы делаете позже, так это печатаете адрес каждого следующего элемента типа float в вашем массиве. Так как размер float в вашем случае ровно 4 байта, то каждый следующий адрес будет равен предыдущему +4. Например, 0x11fe010 + 0x4 = 0x11FE014. Конечно, адрес 0x11FE014 не кратен 0x10. Если бы вы выровняли все числа с плавающей запятой по 16-байтовой границе, вам пришлось бы тратить 16 / 4 - 1 байтов на элемент. Дважды проверьте требования к встроенным функциям, которые вы используете.

person Community    schedule 18.06.2012

Насколько я знаю, и 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 ) );
person j4x    schedule 18.06.2012

Я нашел этот код в Википедии:

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)
person Benoit    schedule 18.06.2012

Я лично считаю, что ваш код правильный и подходит для кода Intel SSE. Когда вы загружаете данные в регистр XMM, я полагаю, что процессор может загрузить только 4 последовательных данных с плавающей запятой из основной памяти, причем первый из них выровнен по 16 байтам.

Короче говоря, я считаю, что вы сделали именно то, что хотели.

person gohongyi    schedule 28.10.2013

вы также можете использовать это в VS.

__declspec(align(16)) struct x {
    long long a;
    long long b;
    char c;
};

вместо этого

struct x {
    float y;
} __attribute__((aligned(16)));
person 정등혁    schedule 16.11.2020