Какой наиболее эффективный способ умножить 4 числа с плавающей точкой на 4 с использованием SSE?
Вопрос
В настоящее время у меня есть следующий код:
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 байт, сработало бы это вообще?Поскольку массивы распределены в стеке, правда ли, что их выравнивание практически невозможно?
см. выбранный ответ для этого сообщения: Выровнены ли переменные стека по GCC __attribute__((aligned(x)))?
(2) Можно ли вообще провести рефакторинг кода, чтобы сделать его более эффективным?Что, если я помещу в регистры оба массива с плавающей запятой, а не только один?
Спасибо
Решение
если бы я БЫЛ выровнять массивы по границам 16 байт, это вообще сработало бы?Поскольку массивы распределены в стеке, правда ли, что их выравнивание практически невозможно?
Требуется, чтобы выравнивание по стеку работало.В противном случае встроенные функции не будут работать.Я предполагаю, что сообщение, которое вы процитировали, было связано с непомерным значением, которое он выбрал для значения выравнивания.
до 2:
Нет, разницы в производительности быть не должно.Видеть это сайт для таймингов инструкций нескольких процессоров.
Как работает выравнивание переменных стека:
push ebp
mov ebp, esp
and esp, -16 ; fffffff0H
sub esp, 200 ; 000000c8H
А и выравнивает начало стека по 16 байтам.
Другие советы
Напишите это на C, используйте
gcc -S -mssse3
если у вас довольно последняя версия gcc.
(1) если бы я БЫЛ выровнять массивы по границам 16 байт, сработало бы это вообще?Поскольку массивы распределены в стеке, правда ли, что их выравнивание практически невозможно?
Нет, выровнять указатель стека довольно просто, используя and
:
and esp, 0xFFFFFFF0 ; aligned on a 16-byte boundary
Но вам следует использовать то, что предоставляет GCC, например, 16-байтовый тип или __attribute__
для настройки выравнивания.
Предоставляет ли 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 };
а затем вместо этого используйте мовапы :)
Использование встроенных функций происходит намного быстрее, особенно при оптимизации.Я написал простой тест и сравнил обе версии (asm и встроенную).
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 Процессор TimeStamps ASM версия ~ 1000 Proc TimeMests
Вы можете проверить это на своей машине
О рефакторинге.Вы можете использовать встроенный.Пример:
#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.