2012-02-13 1 views
4

Предположим, у меня есть два вектора, представленные двумя массивами типа double, каждый из которых равен 2. Я бы хотел добавить соответствующие позиции. Предположим, векторы i0 и i1, я хотел бы добавить i0[0] + i1[0] и i0[1] + i1[1] вместе.SSE Загрузка и добавление

Поскольку тип double, мне нужны два регистра. Хитрость заключалась бы в том, чтобы поставить i0[0] и i1[0], и i0[1] и i1[1] в другой и просто добавьте реестр с собой.

Мой вопрос, если я позвоню _mm_load_ps(i0[0]) и затем _mm_load_ps(i1[0]), будет это место их в верхних и нижних 64 бит отдельно, или он будет заменить регистр со вторым load? Как бы поместить оба дубликата в один и тот же регистр, поэтому я могу позвонить add_ps после?

Спасибо,

ответ

7

Я думаю, что вы хотите это:

double i0[2]; 
double i1[2]; 

__m128d x1 = _mm_load_pd(i0); 
__m128d x2 = _mm_load_pd(i1); 
__m128d sum = _mm_add_pd(x1, x2); 
// do whatever you want to with "sum" now 

Когда вы делаете _mm_load_pd, он ставит первый дубль в нижний 64 бит регистра, а второй в верхнем 16 бит. Таким образом, после нагрузок выше, x1 содержит два значения doublei0[0] и i0[1] (и аналогичные для x2). Вызов _mm_add_pd по вертикали добавляет соответствующие элементы в x1 и x2, поэтому после добавления sum содержит i0[0] + i1[0] в своих младших 64 битах и ​​i0[1] + i1[1] в своих верхних 64 битах.

Edit: Я должен отметить, что нет никакой пользы для использования _mm_load_pd вместо _mm_load_ps. Как показывают названия функций, ассортимент pd явно загружает два упакованных двойника, а версия ps загружает четыре упакованных поплавка с одной точностью. Так как это чисто бит-битовые движения памяти, и оба они используют блок с плавающей запятой SSE, нет никакого штрафа за использование _mm_load_ps для загрузки в double данных. И есть преимущество _mm_load_ps: его кодировка команд составляет один байт, который меньше _mm_load_pd, поэтому он более эффективен из-за смысла кэша команд (и, возможно, декодирования команд, я не эксперт по всем тонкостям современных процессоров x86). Приведенный выше код, используя _mm_load_ps будет выглядеть так:

double i0[2]; 
double i1[2]; 

__m128d x1 = (__m128d) _mm_load_ps((float *) i0); 
__m128d x2 = (__m128d) _mm_load_ps((float *) i1); 
__m128d sum = _mm_add_pd(x1, x2); 
// do whatever you want to with "sum" now 

Там нет функции подразумевается слепков; он просто заставляет компилятор переинтерпретировать содержимое регистра SSE как удерживающий двойные символы вместо float, чтобы он мог быть передан в арифметическую функцию с двойной точностью _mm_add_pd.

+0

Можно, конечно, использовать '_mm_load_ps', но вы рискуете снижение производительности на гипотетическом будущем процессор, который выполнен таким образом, что есть домен байпас штраф между одно- и операции с плавающей запятой с двойной точностью. Я не знаю никаких планов для такого процессора, но это не означает, что никто никогда не будет реализован; поэтому существуют различные операции загрузки. По правде говоря, это пустая возможность, но зачем рисковать? –

+0

Я согласен с тем, что существует риск ухудшения производительности для будущего процессора. Я бы предположил, что можно рассмотреть (например, меру) любое преимущество в производительности, которое должно быть получено с помощью «MOVPS» вместо «MOVPD» в зависимости от конкретного приложения. Если есть возможность использовать его сегодня, и нет никаких указаний на надвигающуюся архитектуру, для которой было бы наказание за это, я бы сделал это. Подобные нагрузки можно легко абстрагировать в тандеме, чтобы в будущем можно было автоматически переключиться на другую реализацию. –

3

Префикс _ps является аббревиатурой от «упакованный сингл», то есть он предназначен для использования с плавающей точкой с одной точностью, а не с двойной точностью.

Вместо этого вы хотите _mm_load_pd(). Эта функция принимает ориентированный по 16 байт указатель на первый элемент массива из двух double s и загружает их оба.Таким образом, вы будете использовать это как так:

__m128d v0 = _mm_load_pd(i0); 
__m128d v1 = _mm_load_pd(i1); 

v0 = _mm_add_pd(v0, v1); 
+0

'_mm_load_ps' фактически может использоваться с значениями двойной точности (и есть преимущества для этого); см. мой ответ. –