如何将 8 字节长整数的每个字节相加?
Posted
技术标签:
【中文标题】如何将 8 字节长整数的每个字节相加?【英文标题】:How to add each byte of an 8-byte long integer? 【发布时间】:2013-08-27 18:42:56 【问题描述】:我正在学习如何在视频应用程序中使用英特尔 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;
【问题讨论】:
请添加您的代码和期望的结果 单个 8 字节整数?psadbw
其中另一个操作数为零。
或者......旧的“通过乘法求水平字节求和”的技巧 - ((PackedWord * 0x0101010101010101ULL) >> 56)
也不能工作吗?
另一个操作数为零的psadbw起作用了。
【参考方案1】:
根据@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);
【讨论】:
+1 今天学习新知识。看起来它很容易适应 _uint128_t。 通常最好使用 SSE2,而不是 MMX,即使您只需要一个 64 位水平和,而不是两个。那么在返回之前您不需要 EMMS。__m128i result = _mm_sad_epu8(_mm_cvtsi64x_si128(pw), _mm_setzero_si128);
应该编译成相同的 MOVQ / PXOR-zeroing / PSADBW / MOVD。我忘记了 64x
/ 64
内在函数中的哪一个可用于 32 位代码,但 MOVQ(作为从内存加载)绝对有效。【参考方案2】:
您可以在一对减少后通过水平相乘来做到这一点:
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);
与执行三个成对归约相比,这会产生更精简的代码。
【讨论】:
【参考方案3】:我不是汇编专家,但这段代码在没有花哨的 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);
这个想法是你首先添加每隔一个字节,然后是每隔一个单词,最后是每隔一个双重世界。
【讨论】:
以上是关于如何将 8 字节长整数的每个字节相加?的主要内容,如果未能解决你的问题,请参考以下文章