2009-08-04 2 views
2

настоящее время у меня следующий код:Какой самый эффективный способ умножить 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 байт, будет он даже работать? Поскольку массивы выделены в стеке, верно ли, что их выравнивание почти невозможно?

видеть выбранный ответ на этот пост: Are stack variables aligned by the GCC __attribute__((aligned(x)))?

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

Благодаря

ответ

1

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

Необходимо, чтобы выравнивание на стеке работало. Иначе внутренне не работало бы. Я бы предположил, что сообщение, которое вы указали, связано с непомерным значением, которое он выбрал для значения выравнивания.

2:

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


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

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

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

1

Предоставляет ли 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

спасибо ; но как указано в этой статье http://stackoverflow.com/questions/841433/gcc-attributealignedx-explanation, кажется невозможным выровнять массивы, выделенные в стеке? (в отличие от глобальных массивов, выделенных в .data) – horseyguy

+0

спасибо за исправление Bastien :) Banister ... можете ли вы попробовать и посмотреть, что произойдет? Если это связано с объяснением, это правильно, тогда было бы невозможно правильно выровнять такие вещи, как double, но они выравниваются. – Goz

+0

да, я скоро ... У меня такое чувство, что связанное объяснение не так, как кажется всем в этом вопросе. всем спасибо! :) – horseyguy

1

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

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

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

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

+0

спасибо за ваш ответ, могли бы вы объяснить мне, как вы можете использовать «и» для выравнивания? я не совсем «получаю» это :) – horseyguy

+1

Напомним, что 'some_bit и 0 = 0' и' a/16 = a >> 4', если a не указано. Использование 'и', как это, установит четыре младших значащих бита в ноль и оставит остальных без изменений. Что произойдет, если вы разделите 'esp' на 16, на самом деле? Он получает правое смещение на 4, а четыре «потерянных» бита - это остаток. Таким образом, эти четыре бита должны быть равны 0, так что 'esp' делится на 16. В действительности происходит то, что он вычитает * не более * 15, так что' esp'% 16 == 0. (Вычитание из 'esp' означает выделение больше места в стеке). –

7

записать его в C, используйте

gcc -S -mssse3 

, если у вас есть достаточно свежая версия GCC.

+0

какой код C будет скомпилирован для этих инструкций sse? у вас есть пример? – horseyguy

+1

float a [4] = {10, 20, 30, 40}; поплавок b [4] = {0,1, 0,1, 0,1, 0,1}; int foo (void) { int i; для (i = 0; i <4; i ++) a [i] * = b [i]; } Скомпилируйте, как показано, и просмотрите файл .s. – xcramps

+0

интересно, спасибо! – horseyguy

0

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

#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) может быть работать быстрее, чем ассемблере.

+0

Насколько быстрее вы считаете, что это бежит? Вы могли бы сравнить? – toxicate20

+0

смотреть следующее сообщение, я тестирую его – AlekseyM

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 прок временные метки

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