Инструкции SSE для добавления всех элементов массива

Я новичок в инструкциях SSE2. Я нашел инструкцию _mm_add_epi8, которая может добавить два элемента массива. Но мне нужна инструкция SSE, которая может добавлять все элементы массива.

Я пытался разработать эту концепцию, используя этот код:

#include <iostream>
#include <conio.h>
#include <emmintrin.h>

void sse(unsigned char* a,unsigned char* b); 

void main()
{
    /*unsigned char *arr;
    arr=(unsigned char *)malloc(50);*/

    unsigned char arr[]={'a','b','c','d','e','f','i','j','k','l','m','n','o','p','q','r','a','b','c','d','e','f','i','j','k','l','m','n','o','p','q','r'};
    unsigned char *next_arr=arr+16;
    for(int i=0;i<16;i++)
          printf("%d,%c   ",next_arr[i],next_arr[i]);
    sse(arr,next_arr);

    getch();
}

void sse(unsigned char* a,unsigned char* b)                                                                                                                                                                          
{                                                                                                                                                                                                                                                                                                                                                                                            
  __m128i* l = (__m128i*)a;                                                                                                                                                                                      
  __m128i* r = (__m128i*)b; 
  __m128i result;

      result= _mm_add_epi8(*l, *r);

      unsigned char *p;
         p=(unsigned char *)&result;

        for(int i=0;i<16;i++)
          printf("%d ",p[i]);

         printf("\n");
         l=(__m128i*)p;
         r=(__m128i*)(p+8);         
         result=_mm_add_epi8(*l, *r);
         p=(unsigned char *)&result;
         printf("%d ",p[0]);

         l=(__m128i*)p;
         r=(__m128i*)(p+4);
         result=_mm_add_epi8(*l, *r);
         p=(unsigned char *)&result;
         l=(__m128i*)p;
         r=(__m128i*)(p+2);
         result=_mm_add_epi8(*l, *r);
         p=(unsigned char *)&result;
         l=(__m128i*)p;
         r=(__m128i*)(p+1);
         result=_mm_add_epi8(*l, *r);
          p=(unsigned char *)&result;
            printf("result =%d ",p[0]);
}

Так может ли кто-нибудь сказать мне, как можно добавить все элементы массива с помощью инструкций SSE2?

Любая помощь будет оценена.


person geeta    schedule 07.06.2012    source источник
comment
Закрыт как дубликат, потому что psadbw значительно более эффективен для суммирования 8-битных элементов без переполнения, и ответ там использует это. Используйте его с paddd или paddq для больших массивов.   -  person Peter Cordes    schedule 07.11.2017


Ответы (1)


Если вы просто хотите суммировать все элементы массива, вам нужно загрузить данные, распаковать их до большего размера элемента, а затем просуммировать распакованные элементы. Обратите внимание, что вы можете поддерживать несколько частичных сумм до завершения цикла, а затем просто выполнить одну окончательную сумму этих частичных сумм. Например:

uint32_t sum_array(const uint8_t a[], int n)
{
    const __m128i vk0 = _mm_set1_epi8(0);       // constant vector of all 0s for use with _mm_unpacklo_epi8/_mm_unpackhi_epi8
    const __m128i vk1 = _mm_set1_epi16(1);      // constant vector of all 1s for use with _mm_madd_epi16
    __m128i vsum = _mm_set1_epi32(0);           // initialise vector of four partial 32 bit sums
    uint32_t sum;
    int i;

    for (i = 0; i < n; i += 16)
    {
        __m128i v = _mm_load_si128(&a[i]);      // load vector of 8 bit values
        __m128i vl = _mm_unpacklo_epi8(v, vk0); // unpack to two vectors of 16 bit values
        __m128i vh = _mm_unpackhi_epi8(v, vk0);
        vsum = _mm_add_epi32(vsum, _mm_madd_epi16(vl, vk1));
        vsum = _mm_add_epi32(vsum, _mm_madd_epi16(vh, vk1));
                                                // unpack and accumulate 16 bit values to
                                                // 32 bit partial sum vector

    }
    // horizontal add of four 32 bit partial sums and return result
    vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 8));
    vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 4));
    sum = _mm_cvtsi128_si32(vsum);
    return sum;
}

Обратите внимание, что в приведенном выше коде есть один неочевидный трюк — вместо дальнейшей распаковки каждого 16-битного вектора в пару 32-битных векторов (требуется 4 инструкции распаковки), а затем с использованием четырех 32-битных добавлений (еще 4 инструкции), мы используем _mm_madd_epi16 (PMADDWD) с множителем 1 и _mm_add_epi32, чтобы фактически дать нам бесплатную распаковку, поэтому мы получаем тот же результат, используя 4 инструкции вместо 8.

Также обратите внимание, что входной массив a[] должен быть выровнен по 16 байтам, а n должен быть кратен 16.

person Paul R    schedule 07.06.2012
comment
Спасибо за ответ. Ваш код показывает ошибку в строках 10,11,13,14 и 17. Инструкция _mm_madd_epi16 не может принимать 3 аргумента. А вк0 не определено? Пожалуйста, устраните эти ошибки. - person geeta; 07.06.2012
comment
Извините, это то, что происходит, когда вы берете какой-то рабочий код и пытаетесь отредактировать его до простого примера - я думаю, что теперь это более или менее исправлено. - person Paul R; 07.06.2012
comment
Спасибо большое.. Работает... :) - person geeta; 07.06.2012
comment
К вашему сведению, я проверил это на процессоре Intel Xeon W3550 3,07 ГГц, и он показал ускорение на 37% по сравнению с простым циклом: сумма = 0; для (i=0; i‹n; i++) sum += array[i]; - person cape1232; 20.02.2013
comment
Однако при использовании в функции для вычисления суммы абсолютных разностей с нулевым средним (компьютерное зрение), где единственным изменением было использование оптимизированного кода по сравнению с приведенным выше наивным кодом для вычисления среднего значения вектора, результат был в 10 раз быстрее. . - person cape1232; 20.02.2013
comment
@PaulR Я бы очень хотел научиться это делать. Кажется, я не могу найти учебник или вводный документ, который объясняет это хорошо. У Вас есть какие-то предложения? - person user24205; 11.11.2016
comment
@ user24205: если вы погуглите, есть несколько руководств, но я бы также посоветовал поискать тег [sse] здесь, в StackOverflow, так как есть много хороших вопросов и ответов, охватывающих весь диапазон от начального уровня до эксперта. - person Paul R; 11.11.2016
comment
psadbw против нуля намного быстрее для 8-битных целых элементов, особенно без знака. Опубликую ответ, если я доберусь до него. Или нет, поскольку уже есть stackoverflow.com/questions/10932550/ - person Peter Cordes; 07.11.2017
comment
Да, решение @harold в связанном вопросе - это путь... - person Paul R; 07.11.2017