Я думаю, что вы хотите, это:
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
содержит два значения double
i0[0]
и i0[1]
(и аналогично для x2
). Вызов _mm_add_pd
по вертикали добавляет соответствующие элементы в x1
и x2
, поэтому после сложения sum
содержит i0[0] + i1[0]
в своих младших 64 битах и i0[1] + i1[1]
в своих старших 64 битах.
Редактировать: Я должен отметить, что использование _mm_load_pd
вместо _mm_load_ps
не имеет смысла. Как показывают названия функций, разновидность pd
явно загружает два упакованных типа double, а версия 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 как удерживающее double вместо плавающих, так что его можно передать в арифметическую функцию двойной точности _mm_add_pd
.