Как да добавя всеки байт от 8-байтово цяло число?

Уча се как да използвам инструкциите на Intel MMX и SSE във видео приложение. Имам 8-байтова дума и бих искал да добавя всичките 8 байта и да създам едно цяло число като резултат. Простият метод е серия от 7 смени и добавяния, но това е бавно. Кой е най-бързият начин да направите това? Има ли MMX или SSE инструкция за това?

Това е бавният начин да го направите

unsigned long PackedWord = whatever....
int byte1 = 0xff & (PackedWord);
int byte2 = 0xff & (PackedWord >> 8);
int byte3 = 0xff & (PackedWord >> 16);
int byte4 = 0xff & (PackedWord >> 24);
int byte5 = 0xff & (PackedWord >> 32);
int byte6 = 0xff & (PackedWord >> 40);
int byte7 = 0xff & (PackedWord >> 48);
int byte8 = 0xff & (PackedWord >> 56);
int sum = byte1 + byte2 + byte3 + byte4 + byte5 + byte6 + byte7 + byte8;

person JB_User    schedule 27.08.2013    source източник
comment
моля, добавете своя код и очаквания резултат   -  person tim    schedule 27.08.2013
comment
Едно цяло число от 8 байта?   -  person Jiminion    schedule 27.08.2013
comment
psadbw където другият операнд е нула.   -  person harold    schedule 27.08.2013
comment
Или ... старото хоризонтално събиране на байтове чрез трик за умножение - няма ли да работи и ((PackedWord * 0x0101010101010101ULL) >> 56)?   -  person FrankH.    schedule 28.08.2013
comment
psadbw, където другият операнд беше нула, работи.   -  person JB_User    schedule 28.08.2013
comment
@FrankH. всъщност не мисля, че го прави, какво ще стане, ако вторият най-висок байт препълни? Работи като последната стъпка в popcnt, защото тогава това препълване никога не може да се случи   -  person harold    schedule 30.08.2013


Отговори (3)


Въз основа на предложението на @harold, бихте искали нещо като:

#include <emmintrin.h>

inline int bytesum(uint64_t pw)
{
  __m64 result = _mm_sad_pu8(*((__m64*) &pw), (__m64) 0LLU); // aka psadbw
  return _mm_cvtsi64_si32(result);
}
person nickie    schedule 27.08.2013
comment
+1 Научете нещо ново днес. Изглежда, че лесно ще се адаптира към _uint128_t. - person chux - Reinstate Monica; 28.08.2013
comment
Обикновено е по-добре да използвате SSE2, а не MMX, дори ако имате нужда само от една 64-битова хоризонтална сума, а не от две. Тогава нямате нужда от EMMS, преди да се върнете. __m128i result = _mm_sad_epu8(_mm_cvtsi64x_si128(pw), _mm_setzero_si128); трябва да се компилира към същия MOVQ / PXOR-нулиране / PSADBW / MOVD. Забравих коя от вътрешните 64x / 64 е налична за 32-битов код, но MOVQ (като зареждане от паметта) определено работи. - person Peter Cordes; 24.09.2016

Можете да направите това с хоризонтално събиране по умножение след едно намаление по двойки:

uint16_t bytesum(uint64_t x) {
    uint64_t pair_bits = 0x0001000100010001LLU;
    uint64_t mask = pair_bits * 0xFF;

    uint64_t pair_sum = (x & mask) + ((x >> 8) & mask);
    return (pair_sum * pair_bits) >> (64 - 16);
}

Това създава много по-икономичен код, отколкото правенето на три редукции по двойки.

person Veedrac    schedule 23.09.2016

Аз не съм асемблер, но този код трябва да е малко по-бърз на платформи, които нямат фантастични SIMD инструкции:

#include <stdint.h>

int bytesum(uint64_t pw) {
    uint64_t a, b, mask;

    mask = 0x00ff00ff00ff00ffLLU;
    a = (pw >> 8) & mask;
    b = pw & mask;
    pw = a + b;

    mask = 0x0000ffff0000ffffLLU;
    a = (pw >> 16) & mask;
    b = pw & mask;
    pw = a + b;

    return (pw >> 32) + (pw & 0xffffffffLLU);
}

Идеята е, че първо добавяте всеки друг байт, след това всяка друга дума и накрая всеки друг двоен свят.

person fuz    schedule 27.08.2013