C 如何将8字节长整数的每个字节相加?

C 如何将8字节长整数的每个字节相加?,c,assembly,sse,mmx,C,Assembly,Sse,Mmx,我正在学习如何在视频应用程序中使用英特尔MMX和SSE指令。我有一个8字节的字,我想把所有8个字节相加,然后生成一个整数作为结果。简单的方法是一系列的7个移位和加法,但速度很慢。最快的方法是什么?对此是否有MMX或SSE指令 这是做这件事的慢方法 unsigned long PackedWord = whatever.... int byte1 = 0xff & (PackedWord); int byte2 = 0xff & (PackedWord >> 8); i

我正在学习如何在视频应用程序中使用英特尔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;

我不是汇编专家,但在没有高级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);
}
#包括
内部字节数(uint64\u t pw){
uint64_t a、b、遮罩;
掩码=0x00FF00FF00FF00FFLU;
a=(pw>>8)和掩码;
b=pw和面罩;
pw=a+b;
掩码=0x0000FFFF0000FFFFLU;
a=(pw>>16)和掩码;
b=pw和面罩;
pw=a+b;
返回(pw>>32)+(pw&0xffffffflu);
}

我们的想法是,先添加其他字节,然后添加其他单词,最后添加其他双世界。

根据@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);
}
#包括
内联整数字节数(uint64\u t pw)
{
__m64结果=_-mm_-sad_-pu8(*(u-m64*)&pw),(u-m64)0LLU);//又名psadbw
返回_mm_cvtsi64_si32(结果);
}

您可以通过一对减少后的水平乘和进行此操作:

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);
}

这会产生比三次成对归约更精简的代码。

请将您的代码和期望的结果添加到单个8字节整数?
psadbw
中,其中另一个操作数为零。或者。。。旧的“通过乘法的水平字节和”技巧-不会
((PackedWord*0x0101010101010101ll)>>56)
也能工作?另一个操作数为零的psadbw。今天+1学习一些新知识。看起来它很容易适应a_uint128_t。通常最好使用SSE2,而不是MMX,即使您只需要一个64位水平和,而不是两个。那你回来之前就不需要EMM了<代码>\uuuuM128i结果=\uMM\uSAD\uEPU8(\uMM\uCVTSI64x\uSI128(pw),\uMM\uSetZero\uSI128)应编译为相同的MOVQ/px或zeroning/PSADBW/MOVD。我忘了哪个
64x
/
64
内部函数可用于32位代码,但MOVQ(作为内存加载)肯定可以工作。