C - Как получить доступ к элементам вектора, используя векторное расширение GCC SSE

Обычно я работаю с 3D-векторами, используя следующие типы:

typedef vec3_t float[3];

инициализация векторов с помощью чего-л. как:

vec3_t x_basis = {1.0, 0.0, 0.0};
vec3_t y_basis = {0.0, 1.0, 0.0};
vec3_t z_basis = {0.0, 0.0, 1.0};

и доступ к ним с помощью чего-л. как:

x_basis[X] * y_basis[X] + ...

Теперь мне нужна векторная арифметика с использованием инструкций SSE. У меня есть следующий код:

typedef float v4sf __attribute__ ((mode(V4SF)))
int main(void)
{
    v4sf   a,b,c;
    a = (v4sf){0.1f,0.2f,0.3f,0.4f};
    b = (v4sf){0.1f,0.2f,0.3f,0.4f};
    c = (v4sf){0.1f,0.2f,0.3f,0.4f};
    a = b + c;
    printf("a=%f \n", a);
    return 0;
}

GCC поддерживает такой способ. Но... Во-первых, в результате я получаю 0,00000. Во-вторых, я не могу получить доступ к элементам таких векторов. Мой вопрос: как я могу получить доступ к элементам таких векторов? мне нужно что-л. например, a[0] для доступа к элементу X, a[1] для доступа к элементу Y и т. д.

PS: я компилирую этот код, используя:

gcc -msse testgcc.c -o testgcc

person psihodelia    schedule 20.11.2009    source источник
comment
Вопросы были не такими сложными, и я не считаю себя экспертом gcc. В следующий раз используйте более безобидный заголовок, я чуть не пропустил вопрос.   -  person Gunther Piez    schedule 20.11.2009


Ответы (3)


Безопасным и рекомендуемым способом доступа к элементам является объединение, а не каламбур типа указателя, который обманывает механизмы обнаружения алиасинга компилятора и может привести к нестабильности кода.

union Vec4 {
    v4sf v;
    float e[4];
};

Vec4 vec;
vec.v = (v4sf){0.1f,0.2f,0.3f,0.4f};
printf("%f %f %f %f\n", vec.e[0], vec.e[1], vec.e[2], vec.e[3]);

person Gunther Piez    schedule 20.11.2009
comment
нет, Elder_george привел более практичный пример - достаточно безопасно, если вы реализуете его совет в макросе или в строке - person psihodelia; 21.11.2009
comment
Кажется, я недостаточно ясно выразился. Каламбур типа с указателями — это плохо, потому что разыменование указателя с типом каламбура нарушит строгие правила псевдонимов. Это приводит к неопределенному поведению. Инлайнинг или макрос не делают его безопаснее. Но вы можете использовать параметр компилятора -fno-strict-aliasing, который как раз и создан для такого неработающего кода. Полученные двоичные файлы могут быть несколько медленнее, потому что вы отказываете компилятору в оптимизации. Читайте об этом и почему это плохо на gcc.gnu.org/onlinedocs/gcc/… в разделе -fstrict-aliasing. - person Gunther Piez; 21.11.2009
comment
Да, @drhirsh прав, для образца, предоставленного @psihodelia, мое решение работает, но оно дает сбой после небольших изменений из-за нарушенного выравнивания. - person elder_george; 21.11.2009

Обратите внимание, что gcc 4.6 теперь поддерживает векторы с индексами:

В C векторы могут быть индексированы, как если бы вектор был массивом с тем же количеством элементов и базовым типом. Доступы за пределами привязки вызывают неопределенное поведение во время выполнения. Предупреждения о внешнем доступе к подписке на вектор можно включить с помощью -Warray-bounds.

person Alexandre Hamez    schedule 05.05.2011
comment
Это работает только на C, а не на C++. Есть нерешенная ошибка: gcc.gnu.org/bugzilla/show_bug.cgi?id =51033 - person David Given; 17.01.2013

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

int main(){
    v4sf a,b,c;
    a = (v4sf){0.1f,0.2f,0.3f,0.4f};
    b = (v4sf){0.1f,0.2f,0.3f,0.4f};
    c = (v4sf){0.1f,0.2f,0.3f,0.4f};
    a = b + c;
    float* pA = (float*) &a;
    printf("a=[%f %f %f %f]\n",pA[0], pA[1], pA[2], pA[3]);
    return 0;
}

P.S.: спасибо за этот вопрос, я не знал, что в gcc есть такая поддержка SSE.

ОБНОВЛЕНИЕ: это решение не работает, когда массивы не выровнены. Решение, предоставленное @drhirsh, свободно от этой проблемы.

person elder_george    schedule 20.11.2009