Использование встроенных функций C и трудности с выравниванием памяти с классами

Итак, я только начинаю использовать встроенные функции C в своем коде, и я создал класс, который в упрощенном виде выглядит следующим образом:

class _Vector3D
{
public:
_Vector3D() 
{
    aVals[0] = _mm_setzero_ps();
    aVals[1] = _mm_setzero_ps();
    aVals[2] = _mm_setzero_ps();
}
~_Vector3D() {}
private:
__m128 aVals[3];
};

Все идет нормально. Но когда я создаю второй класс с членами _Vector3D, у меня возникают проблемы:

class RayPacket
{
public:
RayPacket() {orig = _Vector3D(); dir = _Vector3D(); power = _mm_setzero_ps();}
~RayPacket() {}

RayPacket(_Vector3D origins, _Vector3D directions, float pow)
{
    orig = origins;
    dir = directions;
    power = _mm_set_ps1(pow);
}

_Vector3D orig;
_Vector3D dir;
__m128 power;
};

Я получаю следующую ошибку:

ошибка C2719: 'origins': формальный параметр с __declspec(align('16')) не будет выровнен

указывая на перегрузку конструктора:

RayPacket(_Vector3D origins, _Vector3D directions, float pow)

Так я иду об этом неправильно? Должен ли я использовать вместо этого структуры или я могу заставить его работать с классами?


person Nigel    schedule 28.10.2009    source источник
comment
Почему вы вдруг переключаетесь на float с __m128?   -  person Bill    schedule 28.10.2009
comment
Проблемы с вашими идентификаторами. Идентификаторы, начинающиеся с подчеркивания, за которым следует заглавная буква, и идентификаторы, содержащие два последовательных символа подчеркивания, зарезервированы реализацией. Их использование приведет к проблемам в будущем.   -  person Martin York    schedule 28.10.2009
comment
Просто замечу - у вас, похоже, небольшое недопонимание SSE. Вы называете _Vector3D то, что должно быть матрицей 3x4. каждый из __m128 представляет собой набор из 4 поплавков.   -  person LiraNuna    schedule 28.10.2009
comment
Спасибо, Мартин, я меняю этот идентификатор...   -  person Nigel    schedule 28.10.2009
comment
@Liranuna - Ну, для моих целей это набор из 4 3D-векторов ...   -  person Nigel    schedule 28.10.2009
comment
Во-первых, это набор из трех четырехмерных векторов. Во-вторых, я не думаю, что разумно называть его Vector3D, когда на самом деле это набор векторов (или матрица).   -  person LiraNuna    schedule 28.10.2009


Ответы (3)


Этот ответ основан на документации и предположениях, а не на реальных знаниях. Осторожно!

В документации для __m128 говорится:

Переменные типа _m128 [так в оригинале] автоматически выравниваются по 16-байтовым границам.

Таким образом, использование члена __m128 в вашем классе заставляет компилятор выравнивать экземпляры вашего класса по 16-байтовым границам. Неявно __declspec(align(16)) добавляется к вашему классу, но это не допускается для параметров функции, потому что компилятору сложно (невозможно?) обеспечить выравнивание внутри кадров стека.

В качестве обходного пути попробуйте передать аргументы конструктора по ссылке:

RayPacket(_Vector3D const &origins, _Vector3D const &directions, float pow)
person Thomas    schedule 28.10.2009

Я думаю, проблема в том, что компилятор не может гарантировать, что указатель стека будет правильно выровнен, когда он создает объект _Vector3D в стеке для передачи конструктору.

В 32-битных системах указатели стека обычно гарантированно выровнены по 4 байтам (иногда по 8 байтам), а в 64-битных системах я думаю, что указатель стека обычно гарантированно выровнен по 8 байтам, поэтому компилятор этого не делает. знать, когда он вызовет конструктор, что стек будет правильно выровнен. Возможно, вам потребуется передать указатель или ссылку.

Обратите внимание, что malloc() и друзья, гарантированное выравнивание, возвращаемое для блока, иногда не гарантирует, что оно сможет обрабатывать специальные типы, подобные этому. В этом случае платформа будет иметь специальную функцию распределения для размещения этих объектов.

Дополнительные сведения о MSVC см. в следующем документе (http://msdn.microsoft.com/en-us/library/aa290049.aspx):

Выравнивание стека

На обеих 64-битных платформах вершина каждого кадра стека выравнивается по 16 байтам. Хотя при этом используется больше места, чем необходимо, это гарантирует, что компилятор сможет разместить все данные в стеке таким образом, чтобы все элементы были выровнены.

Компилятор x86 использует другой метод выравнивания стека. По умолчанию стек выровнен по 4 байтам. Хотя это экономит место, вы можете видеть, что есть некоторые типы данных, которые необходимо выровнять по 8 байтам, и что для получения хорошей производительности иногда требуется выравнивание по 16 байтам. В некоторых случаях компилятор может определить, что динамическое выравнивание стека по 8 байтам будет полезным, особенно когда в стеке есть двойные значения.

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

Примечание. Недостатком динамического 8-байтового выравнивания стека с точки зрения производительности является то, что пропуск указателя кадра (/Oy) эффективно отключается. Регистр EBP должен использоваться для обращения к стеку с динамическим 8-байтовым стеком, поэтому его нельзя использовать в качестве общего регистра в функции.

В приведенной выше статье также есть некоторая информация о специальных функциях кучи, которые обеспечивают гарантии выравнивания выше, чем стандартные malloc(), если вам это нужно.

person Michael Burr    schedule 28.10.2009

Попробуйте передать _Vector3D по константной ссылке, например:

RayPacket( const _Vector3D& origins, const _Vector3D& directions, float pow );
Это поместит указатели вместо значений в стек вызовов.

person Nikolai Fetissov    schedule 28.10.2009