Какой самый эффективный способ умножить 4 числа на 4 числа с помощью SSE? - PullRequest
2 голосов
/ 04 августа 2009

У меня сейчас есть следующий код:

float a[4] = { 10, 20, 30, 40 };
float b[4] = { 0.1, 0.1, 0.1, 0.1 };
asm volatile("movups (%0), %%xmm0\n\t"
             "mulps (%1), %%xmm0\n\t"             
             "movups %%xmm0, (%1)"             
             :: "r" (a), "r" (b));

У меня прежде всего несколько вопросов:

(1) если бы я БЫЛ выровнять массивы по границам 16 байт, это бы вообще сработало? Поскольку массивы расположены в стеке, верно ли, что их выравнивание практически невозможно?

см. Выбранный ответ для этого сообщения: Согласованы ли переменные стека __attribute GCC ((align (x)))?

(2) Может ли код быть реорганизован вообще, чтобы сделать его более эффективным? Что если я положу оба массива с плавающей точкой в ​​регистры, а не в один?

Спасибо

Ответы [ 6 ]

7 голосов
/ 04 августа 2009

Напишите это в C, используйте

gcc -S -mssse3

если у вас довольно свежая версия gcc.

1 голос
/ 23 ноября 2012

Использование встроенного намного быстрее, особенно с оптимизацией. Я написал простой тест и сравнил обе версии (asm и intrinsic)

unsigned long long time1;
__m128 a1,b1;


a1=_mm_set_ps(10, 20,30,40);
b1=_mm_set_ps(0.1, 0.1, 0.1, 0.1);
float a[4] = { 10, 20, 30, 40 };
float b[4] = { 0.1, 0.1, 0.1, 0.1 };

time1=__rdtsc();
a1=_mm_mul_ps(a1,b1);
time1=__rdtsc() - time1 ;
printf("Time: %llu\n",time1);


time1=__rdtsc();
asm volatile("movups (%0), %%xmm0\n\t"
                 "mulps (%1), %%xmm0\n\t"
                 "movups %%xmm0, (%1)"
                 :: "r" (a), "r" (b));
time1=__rdtsc() - time1 ;
printf("Time: %llu\n",time1);

Внутренняя версия 50-60 временных меток процессора Asm Version ~ 1000 временных меток proc

Вы можете проверить это на своей машине

1 голос
/ 04 августа 2009

(1) если бы я БЫЛ выровнять массивы по 16-байтовым границам, это вообще сработало бы? Поскольку массивы расположены в стеке, верно ли, что их выравнивание практически невозможно?

Нет, довольно просто выровнять указатель стека, используя and:

and esp, 0xFFFFFFF0 ; aligned on a 16-byte boundary

Но вы должны использовать то, что обеспечивает GCC, например, 16-байтовый тип или __attribute__ для настройки выравнивания.

1 голос
/ 04 августа 2009

если бы я БЫЛ выровнять массивы по 16-байтовым границам, это вообще сработало бы? Поскольку массивы расположены в стеке, верно ли, что их выравнивание практически невозможно?

Требуется, чтобы выравнивание в стеке работало. В противном случае встроенные функции не будут работать. Я предполагаю, что цитируемый вами пост был связан с непомерным значением, которое он выбрал для значения выравнивания.

до 2:

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


Как работает выравнивание переменных стека:

push    ebp
mov ebp, esp
and esp, -16                ; fffffff0H
sub esp, 200                ; 000000c8H

и выравнивают начало стека до 16 байт.

1 голос
/ 04 августа 2009

Обеспечивает ли GCC поддержку типа данных __m128? Если это так, то это ваш лучший план для гарантирования 16-байтового выравниваемого типа данных. Тем не менее, есть __attribute__((aligned(16))) для выравнивания вещей. Определите ваши массивы следующим образом

float a[4] __attribute__((aligned(16))) = { 10, 20, 30, 40 };
float b[4] __attribute__((aligned(16))) = { 0.1, 0.1, 0.1, 0.1 };

и используйте вместо этого movaps:)

0 голосов
/ 23 ноября 2012

О рефакторинге. Вы можете использовать внутренние. Пример:

#include <emmintrin.h>

int main(void)
{
    __m128 a1,b1;

    a1=_mm_set_ps(10, 20,30,40);
    b1=_mm_set_ps(0.1, 0.1, 0.1, 0.1);

    a1=_mm_mul_ps(a1,b1);

    return 0;
}

При оптимизации gcc (-O2, -O3) он может работать быстрее, чем asm.

...