SIMD/SSE: кратък точков продукт и кратка максимална стойност

Опитвам се да оптимизирам точково произведение на два масива в стил c от contant и малък размер и от тип short.

Прочетох няколко документации за SIMD intrinsics и много публикации в блогове/статии за оптимизация на точков продукт с помощта на този intrisinc.

Въпреки това не разбирам как точков продукт върху къси масиви, използващ тази присъща характеристика, може да даде правилния резултат. Когато правите точковия продукт, изчислените стойности могат да бъдат (и винаги са в моя случай) по-големи от SHORT_MAX, така че има и сума. Следователно ги съхранявам в променлива от двоен тип.

Доколкото разбирам точковия продукт, използващ simd intrinsic, ние използваме __m128i типове променливи и операциите връщат __m128i. И така, това, което не разбирам, е защо не се "прелива" и как резултатът може да се трансформира в тип стойност, който може да се справи с него?

благодаря за вашите съвети


person florian    schedule 09.02.2015    source източник
comment
Защо искате да оптимизирате точковото произведение на два малки масива? Какво друго правите с тези масиви? Какво правите в критичния си цикъл? Някои примери за код биха помогнали.   -  person Z boson    schedule 10.02.2015
comment
Опитвам се да оптимизирам точковото произведение, тъй като това отнема 80% от времето на целия процес: почти 15 милиона различни точкови произведения наведнъж, така че всяко подобрение може да има огромен ефект.   -  person florian    schedule 10.02.2015


Отговори (2)


В зависимост от обхвата на вашите стойности на данни може да използвате вътрешен като _mm_madd_epi16, който извършва умножение/събиране на 16-битови данни и генерира 32-битови термини. След това ще трябва периодично да натрупвате вашите 32-битови условия до 64 бита. Колко често трябва да правите това зависи от обхвата на вашите входни данни, напр. ако това са 12-битови данни за изображение в сива скала, тогава можете да направите 64 итерации по 8 елемента на итерация (т.е. 512 входни точки), преди да има потенциал за препълване. В най-лошия случай обаче, ако вашите входни данни използват пълния 16-битов диапазон, тогава ще трябва да направите допълнителните 64-битови натрупвания на всяка итерация (т.е. на всеки 8 точки).

person Paul R    schedule 09.02.2015
comment
Не съм виждал тази функция, а сега виждам светлината. Приложих точковия продукт и той работи, tihout препълване. Имам увеличение на скоростта от почти 25%, което е огромно за мен! - person florian; 10.02.2015
comment
_mm_madd_epi16 е някъде близо до върха на списъка ми с топ 10 на SSE присъщи за всички времена. ;-) Освен точкови произведения, той е полезен и за редукции на типове статистики, напр. sum(x), sum(x^2) и т.н. Между другото, ако искате да публикувате кода си като нов въпрос, тогава трябва да е възможно да подобрите печалбата си от 25%. - person Paul R; 10.02.2015
comment
@florian, бях забравил за _mm_madd_epi16. Благодаря на Пол, че отново ми го напомни. Би било интересно да видя вашия код и повече от това, което правите, както Пол предложи. Вашата операция вероятно е обвързана с честотната лента на паметта, така че не можете да направите много, освен да промените това, което правите, но е трудно да се каже със сигурност, без да знаете повече за това, което правите. - person Z boson; 11.02.2015
comment
Да, също така си представях, че тези операции са обвързани с честотната лента на паметта. Освен това общият процес е паралелен, така че не очаквам печалба от 25%, а по-малко от това. Но това вече е огромно подобрение (очаквах 2/5%). Ще публикувам кода си по-късно, но защо в друга тема? - person florian; 11.02.2015
comment
и, като друг коментар, съответният код на avx е по-бавен. - person florian; 11.02.2015

Само за протокола, ето как правя точков продукт за 2 int16 масива с размер 36:

double dotprod(const int16_t* source, const int16_t* target, const int size){
#ifdef USE_SSE
    int res[4];
    __m128i* src = (__m128i *) source;
    __m128i* t = (__m128i *) target;
    __m128i s = _mm_madd_epi16(_mm_loadu_si128(src), mm_loadu_si128(t));
    ++src;
    ++t;
    s = _mm_add_epi32(s, _mm_madd_epi16(_mm_loadu_si128(src), _mm_loadu_si128(t)));
    ++src;
    ++t;
    s = _mm_add_epi32(s, _mm_madd_epi16(_mm_loadu_si128(src), _mm_loadu_si128(t)));
    ++src;
    ++t;
    s = _mm_add_epi32(s, _mm_madd_epi16(_mm_loadu_si128(src), _mm_loadu_si128(t)));

    /* return the sum of the four 32-bit sub sums */
    _mm_storeu_si128((__m128i*)&res, s);
    return res[0] + res[1] + res[2] + res[3] + source[32] * target[32] + source[33] * target[33] + source[34] * target[34] + source[35] * target[35];
#elif USE_AVX
    int res[8];
    __m256i* src = (__m256i *) source;
    __m256i* t = (__m256i *) target;
    __m256i s = _mm256_madd_epi16(_mm256_loadu_si256(src), _mm256_loadu_si256(t));
    ++src;
    ++t;
    s = _mm256_add_epi32(s, _mm256_madd_epi16(_mm256_loadu_si256(src), _mm256_loadu_si256(t)));

    /* return the sum of the 8 32-bit sub sums */
    _mm256_storeu_si256((__m256i*)&res, s);
    return res[0] + res[1] + res[2] + res[3] + res[4] + res[5] + res[6] + res[7] + source[32] * target[32] + source[33] * target[33] + source[34] * target[34] + source[35] * target[35];
#else
    return source[0] * target[0] + source[1] * target[1] + source[2] * target[2] + source[3] * target[3] + source[4] * target[4]+ source[5] * target[5] + source[6] * target[6] + source[7] * target[7] + source[8] * target[8] + source[9] * target[9] + source[10] * target[10] + source[11] * target[11] + source[12] * target[12] + source[13] * target[13] + source[14] * target[14] + source[15] * target[15] + source[16] * target[16] + source[17] * target[17] + source[18] * target[18] + source[19] * target[19] + source[20] * target[20] + source[21] * target[21] + source[22] * target[22] + source[23] * target[23] + source[24] * target[24] + source[25] * target[25] + source[26] * target[26] + source[27] * target[27] + source[28] * target[28] + source[29] * target[29] + source[30] * target[30] + source[31] * target[31] + source[32] * target[32] + source[33] * target[33] + source[34] * target[34] + source[35] * target[35];
#endif
}
person florian    schedule 16.02.2015