У меня есть код, который работает со значениями __m128
. Я использую встроенные функции SSE x86-64 для этих значений и обнаружил, что если значения не выровнены в памяти, происходит сбой. Это связано с тем, что мой компилятор (в данном случае clang) генерирует только выровненные инструкции загрузки.
Могу ли я указать своему компилятору генерировать невыровненные нагрузки либо глобально, либо для определенных значений (возможно, с какой-либо аннотацией)?
Причина, по которой у меня невыровненные значения, в первую очередь, заключается в том, что я пытаюсь сэкономить память. У меня struct
примерно такой:
#pragma pack(push, 4)
struct Foobar {
__m128 a;
__m128 b;
int c;
};
#pragma pack(pop)
Затем я создаю массив этих структур. Второй элемент массива начинается с 36 байтов, что не кратно 16.
Я знаю, что могу переключиться на структуру представления массивов или удалить прагму упаковки (за счет увеличения размера структуры с 36 до 48 байтов); но я также знаю, что невыровненные нагрузки в наши дни не так уж дороги, и хотел бы сначала попробовать это.
Обновите, чтобы ответить на некоторые из комментариев ниже:
Мой реальный код был ближе к этому:
struct Vector4 {
__m128 data;
Vector4(__m128 v) : data(v) {}
};
struct Foobar {
Vector4 a;
Vector4 b;
int c;
}
Затем у меня есть некоторые служебные функции, такие как:
inline Vector4 add( const Vector4& a, const Vector4 &b ) {
return Vector4(_mm_add_ps(a.data, b.data));
}
inline Vector4 subtract( const Vector4& a, const Vector4& b ) {
return Vector4(_mm_sub_ps(a.data, b.data));
}
// etc..
Я часто использую эти утилиты вместе. Поддельный пример:
Foobar myArray[1000];
myArray[i+1].b = sub(add(myArray[i].a, myArray[i].b), myArray[i+1].a);
Когда я смотрю на ответ "Z Bozon", мой код фактически изменился на:
struct Vector4 {
float data[4];
};
inline Vector4 add( const Vector4& a, const Vector4 &b ) {
Vector4 result;
_mm_storeu_ps(result.data, _mm_add_ps(_mm_loadu_ps(a.data), _mm_loadu_ps(b.data)));
return result;
}
Меня беспокоило то, что, когда служебные функции использовались в комбинации, как указано выше, сгенерированный код мог иметь избыточные инструкции загрузки / сохранения. Оказывается, это не было проблемой. Я протестировал свой компилятор (clang), и он все удалил. Я приму ответ З. Бозона.
__m128
в своей структуре. Используйте, например,float a[4]
и явно выполняйте загрузку и сохранение с помощью_mm_loadu_ps
и_mm_storeu_ps
. - person Z boson   schedule 24.11.2015__m128
, потому что этот тип данных имеет 16-байтовое выравнивание, определенное на языке C. Это означает, что если вы специально не издеваетесь над своим компилятором, он должен убедиться, что значение типа__m128
правильно выровнено. Вы заставляете компилятор сделать его невыровненным, что может противоречить правилам. Я считаю, что использование_mm_storeu_ps
, как предлагает Z-бозон, является единственным надежным решением в таком случае. - person stgatilov   schedule 24.11.2015__m128
, gcc может скопировать каждый элемент массива отдельно. (Давным-давно в gcc произошла регрессия, которая до сих пор не исправлена, afaik, что он копирует структуры поэтапно, а не с более широкими загрузками / хранилищами). В этом случае мы получаем 64b целочисленных копий для обоих: goo.gl/Lm6TGi - person Peter Cordes   schedule 25.11.2015movdqu/a
илиmova/ups
. Думайте о них больше как о способе передачи информации о выравнивании компилятору. Однако вы можете проверить, делает ли ваш компилятор то, что вы ожидаете. clang полностью оптимизирует некоторые магазины / перезагрузки доfloat local_a[4]
, но gcc делает их все. goo.gl/Mxe7oR. В любом случае все компиляторы могут складывать выровненные нагрузки в другие инструкции SSE в качестве операндов памяти, если они сочтут, что это лучше всего. (С AVX невыровненные грузы тоже могут складываться.) - person Peter Cordes   schedule 25.11.2015