Ответ 1
В этом посте много вопросов. Поскольку руководство по программированию CUDA довольно хорошо объясняет выравнивание в CUDA, я просто объясню несколько вещей, которые не очевидны в руководстве.
Во-первых, причина, по которой ваш компилятор хоста дает вам ошибки, заключается в том, что компилятор хоста не знает, что означает __align(n)__
, поэтому он дает синтаксическую ошибку. Что вам нужно, это добавить в заголовок для вашего проекта что-то вроде следующего.
#if defined(__CUDACC__) // NVCC
#define MY_ALIGN(n) __align__(n)
#elif defined(__GNUC__) // GCC
#define MY_ALIGN(n) __attribute__((aligned(n)))
#elif defined(_MSC_VER) // MSVC
#define MY_ALIGN(n) __declspec(align(n))
#else
#error "Please provide a definition for MY_ALIGN macro for your host compiler!"
#endif
Итак, я должен иметь два разных определения для структур хоста и устройства?
Нет, просто используйте MY_ALIGN(n)
, как этот
struct MY_ALIGN(16) pt { int i, j, k; }
Например, как должны быть выровнены следующие два?
Во-первых, __align(n)__
(или любой из компонентов компилятора хоста) обеспечивает, чтобы память для структуры начиналась с адреса в памяти, который кратен n
байтам. Если размер структуры не кратен n
, тогда в массиве этих структур будет добавлено дополнение, чтобы гарантировать, что каждая структура правильно выровнена. Чтобы выбрать правильное значение для n
, вы хотите свести к минимуму требуемое заполнение. Как поясняется в руководстве по программированию, аппаратное обеспечение требует, чтобы каждый поток читал слова, выровненные с 1,2,4, 8 или 16 байтами. Так что...
struct MY_ALIGN(16) {
int a;
int b;
int c;
int d;
float* el;
};
В этом случае допустим, что мы выбираем 16-байтовое выравнивание. На 32-битной машине указатель занимает 4 байта, поэтому структура занимает 20 байтов. 16-байтовое выравнивание будет тратить 16 * (ceil(20/16) - 1) = 12
байтов на каждую структуру. На 64-битной машине он будет тратить всего 8 байтов на каждую структуру из-за 8-байтового указателя. Мы можем уменьшить количество отходов с помощью MY_ALIGN(8)
. Компромисс будет заключаться в том, что аппаратное обеспечение должно будет использовать 3 8-байтовые нагрузки вместо 2 16-байтовых нагрузок для загрузки структуры из памяти. Если вы не испытываете недостатка в нагрузках, это, вероятно, целесообразный компромисс. Обратите внимание, что вы не хотите выровнять меньше 4 байтов для этой структуры.
struct MY_ALIGN(16) {
int a;
int b
int c
int d
float* i;
float* j;
float* k;
};
В этом случае с выравниванием по 16 байт вы тратите только 4 байта на каждую структуру на 32-битные машины или 8 на 64-разрядные машины. Это потребует двух 16-байтовых нагрузок (или 3 на 64-битной машине). Если мы выровняемся с 8 байтами, мы можем полностью исключить отходы с 4-байтовым выравниванием (8-байтовые на 64-битных машинах), но это приведет к чрезмерным нагрузкам. Опять же, компромиссы.
или, как должна быть выровнена структура с 6 поплавками?
Опять же, компромиссы: либо отбросить 8 байт на одну структуру, либо потребовать две нагрузки для каждой структуры.
или 4 целых числа?
Здесь нет компромиссов. MY_ALIGN(16)
.
снова, я не использую массивы тех, но все же я определяю множество переменных с этими структурами в ядрах или _ функции устройства _.
Хммм, если вы не используете массивы из них, вам может вообще не понадобиться выровнять. Но как вы их назначаете? Как вы, вероятно, видите, все эти отходы важны, чтобы беспокоиться о них, это еще одна веская причина для поддержки структур массивов над массивами структур.