MMX操作(加16bit没做)
Posted
技术标签:
【中文标题】MMX操作(加16bit没做)【英文标题】:MMX operation (add 16bit is not done) 【发布时间】:2011-06-29 11:29:19 【问题描述】:我得到了一些包含无符号字符的向量,这些字符代表帧中的像素。 我让这个功能在没有 MMX 改进的情况下工作,但我对 MMX 不起作用感到沮丧......所以:
我需要添加两个无符号字符(总和需要作为 16 位而不是 8 位来完成,因为已知无符号字符从 0 到 255)并将它们除以 2(右移 1)。到目前为止我所做的代码如下,但是值是错误的,adds_pu16 没有添加 16bit 只是 8:
MM0 = _mm_setzero_si64(); //all zeros
MM1 = TO_M64(lv1+k); //first 8 unsigned chars
MM2 = TO_M64(lv2+k); //second 8 unsigned chars
MM3 =_mm_unpacklo_pi8(MM0,MM1); //get first 4chars from MM1 and add Zeros
MM4 =_mm_unpackhi_pi8(MM0,MM1); //get last 4chars from MM1 and add Zeros
MM5 =_mm_unpacklo_pi8(MM0,MM2); //same as above for line 2
MM6 =_mm_unpackhi_pi8(MM0,MM2);
MM1 = _mm_adds_pu16(MM3,MM5); //add both chars as a 16bit sum (255+255 max range)
MM2 = _mm_adds_pu16(MM4,MM6);
MM3 = _mm_srai_pi16(MM1,1); //right shift (division by 2)
MM4 = _mm_srai_pi16(MM2,1);
MM1 = _mm_packs_pi16(MM3,MM4); //pack the 2 MMX registers into one
v2 = TO_UCHAR(MM1); //put results in the destination array
新进展: 谢谢你的king_nak!! 我写了一个我想要做的简单版本:
int main()
char A[8]=255,155,2,3,4,5,6,7;
char B[8]=255,155,2,3,4,5,6,7;
char C[8];
char D[8];
char R[8];
__m64* pA=(__m64*) A;
__m64* pB=(__m64*) B;
__m64* pC=(__m64*) C;
__m64* pD=(__m64*) D;
__m64* pR=(__m64*) R;
_mm_empty();
__m64 MM0 = _mm_setzero_si64();
__m64 MM1 = _mm_unpacklo_pi8(*pA,MM0);
__m64 MM2 = _mm_unpackhi_pi8(*pA,MM0);
__m64 MM3 = _mm_unpacklo_pi8(*pB,MM0);
__m64 MM4 = _mm_unpackhi_pi8(*pB,MM0);
__m64 MM5 = _mm_add_pi16(MM1,MM3);
__m64 MM6 = _mm_add_pi16(MM2,MM4);
printf("SUM:\n");
*pC= _mm_add_pi16(MM1,MM3);
*pD= _mm_add_pi16(MM2,MM4);
for(int i=0; i<8; i++) printf("\t%d ", (C[i])); printf("\n");
for(int i=0; i<8; i++) printf("\t%d ", D[i]); printf("\n");
printf("DIV:\n");
*pC= _mm_srai_pi16(MM5,1);
*pD= _mm_srai_pi16(MM6,1);
for(int i=0; i<8; i++) printf("\t%d ", (C[i])); printf("\n");
for(int i=0; i<8; i++) printf("\t%d ", D[i]); printf("\n");
MM1= _mm_srai_pi16(MM5,1);
MM2= _mm_srai_pi16(MM6,1);
printf("Final Result:\n");
*pR= _mm_packs_pi16(MM1,MM2);
for(int i=0; i<8; i++) printf("\t%d ", (R[i])); printf("\n");
return(0);
结果是:
总和:
-2 1 54 1 4 0 6 0
8 0 10 0 12 0 14 0
DIV:
-1 0 -101 0 2 0 3 0
4 0 5 0 6 0 7 0
最终结果:
127 127 2 3 4 5 6 7
好吧,小数字是可以的,而给出 127 的大数字是错误的。这是一个问题,我做错了什么:s
【问题讨论】:
与您的问题没有直接关系,但不是 srai 符号扩展结果吗?好的,将两个 8bit 的 uint 加在一起最多需要 9bit,所以这不是问题。 既然您一次处理 16 个字节,为什么不使用 SSE 而不是 MMX? 因为要求是使用 MMX :s Whit SSE 我可以一步完成两个值之间的平均值,这很容易:S 不幸的是,您使用的算术移位指令是 SSE2...所以如果要求实际上只是 MMX,这将不起作用。如果您至少可以使用 3dnow 或 SSE 指令,则应该使用 PAVGB 计算 8 个值之间的平均值。 对不起,忘了我说的话。 PSRAW 在 MMX 中可用。看错地方了。 【参考方案1】:您应该在_mm_unpacklo_pi8
调用中切换操作数。当您这样做时,值字节位于单词的较高字节中(例如,AB
和 00
打包到 AB00
)。在加法和移位之后,这些值将大于0x7F
,因此被 pack 指令饱和到该值。
使用切换的操作数,对00AB
之类的值进行数学运算,结果将适合有符号字节。
更新:
在您提供更多信息后,我发现问题出在_mm_packs_pi16
上。这是汇编指令packsswb
,它将饱和有符号字节。例如。值 > 127 将设置为 127。(255+255)>>1
是 255,(155+155)>>1
是 155...
请改用_mm_packs_pu16
。这会将值视为无符号字节,并且您会得到所需的结果 (255/155)。
【讨论】:
这不会让你失去 0x7f 和 0xff 之间的所有平均值吗?例如,添加 0xa0 和 0xc0。高扩展,这分别给出 0xa000 和 0xc000。 0xa000 + 0xc000 = 0x16000,将被截断为 0x6000(如果使用饱和加法,则饱和为 0xffff)。 Shift,你得到 0x30,这不是正确的结果。 我做了更多的工作并在上面发布......仍然不知道哪里出了问题。 我已经从有符号的包装变成了无符号的包装,它仍然是错误的:s 结果:-1 -101 2 3 4 5 6 7 这是正确的结果,如 -1 == 255 和 -101 == 155。您只有有符号/无符号和字/字节显示的问题。试试unsigned char x = R[0]
,它会给你255。(你也可以在你的输出循环中写R[i]&0xff
)【参考方案2】:
我想我发现了问题: 解包指令的参数顺序错误。如果您将寄存器作为一个整体来查看,看起来各个字符零扩展为短裤,但实际上,它们是零-填充。只需在每种情况下交换 mm0 和其他寄存器,它应该可以工作。
另外,您不需要饱和添加,普通的 PADDW 就足够了。您将获得的最大值为 0xff+0xff=0x01fe,不必饱和。
编辑:更重要的是,PACKSSWB 并不能完全满足您的需求。 PACKUSWB 是正确的指令,饱和会得到错误的结果。
这是一个解决方案(也将移位替换为逻辑移位并在某些地方使用不同的伪寄存器):
mm0=pxor(mm0,mm0) =[00,00,00,00,00,00,00,00]
mm1 =[a0,10,ff,18,7f,f0,ff,cc]
mm2 =[c0,20,ff,00,70,26,ff,01]
mm3=punpcklbw(mm1,mm0) =[00a0,0010,00ff,0018]
mm4=punpckhbw(mm1,mm0) =[007f,00f0,00ff,00cc]
mm5=punpcklbw(mm2,mm0) =[00c0,0020,00ff,0000]
mm6=punpckhbw(mm2,mm0) =[0070,0026,00ff,0001]
mm5=paddw(mm3,mm5) =[0160,0030,01fe,0018]
mm6=paddw(mm4,mm6) =[00ef,0116,01fe,00cd]
mm3=psrlwi(mm5,1) =[00b0,0018,00ff,000c]
mm4=psrlwi(mm6,1) =[0077,008b,00ff,0066]
mm1=packuswb(mm3,mm4) =[b0,18,ff,0c,77,8b,ff,66]
【讨论】:
【参考方案3】:顺便说一句,您不需要 16 位中间值来计算两个 8 位值的平均值。配方:
(a >> 1) + (b >> 1) + (a & b & 1)
只需要 8 位中间值即可给出正确结果。如果您有 8 位矢量指令可用,也许您可以利用它来提高吞吐量。
【讨论】:
我也尝试过这种方法,但在 MMX 指令集中没有 8 位的移位指令,只有 16/32/64 位 msdn.microsoft.com/en-us/library/s9fcy11x.aspx @Paiva:请注意,您可以通过在更宽的移位之前屏蔽每个字节& 0xfe
来模拟 8 位逻辑移位。以上是关于MMX操作(加16bit没做)的主要内容,如果未能解决你的问题,请参考以下文章