我正在使用一个库,该库使用英特尔的 MMX 单指令、多数据 (SIMD) 指令集来加速整数数组的乘法。我正在使用的函数包含内联汇编,以使用 Intel 处理器中的 MMX SIMD 寄存器并执行乘法运算。
将两个整数数组与函数相乘后,我收到一个数组,其中包含不正确的整数值,应该为负数。但是,当将这些值转换为二进制时,我注意到整数表示 2 的补码中的正确值。整数应该是 16 位长。
更奇怪的是,当两个负整数相乘时,而不是一个正数一个负数,该函数返回一个整数值,当转换为二进制时,添加一个额外位作为最高有效位(将附加位标记到左侧二进制数)。该位的值为 1,但如果您忽略该位,其余位将正确显示预期值。
很难用语言来表达,所以让我举个例子:
我有三个 int 数组 A、B 和 C。
A = {-1, 4, 1, -1, 1, -2, -3, 7},
B = {-1, -1, -1, -1, -1, -1, -1, 1}
C = {0, 0, 0, 0, 0, 0, 0, 0}
当 A 和 B 相乘时,我希望
{1, -4, -1, 1, -1, 2, 3, 7}
存储在 C 中。
然而,在使用图书馆的功能后,我得到
{65537、65532、65535、65537、65535、65538、65539、7}
作为我对 C 的价值观。
二进制的第一个值 65537 是 10000000000000001。如果没有额外的第 17 位,这将等于 1,但即便如此,该值也应该是 1,而不是 65537。二进制的第二个值 65532 是 1111111111111100,它是 2 的补码为-4。这很好,但为什么这个值不只是-4。还要注意最后一个值 7。当不涉及负号时,该函数会给出预期形式的值。
内联程序集是为在 Microsoft Visual Studio 上编译而编写的,但我使用的是带有 -use-msasm 标志的英特尔 c/c++ 编译器。
这是功能代码:
void mmx_mul(void *A, void *B, void *C, int cnt)
{
int cnt1;
int cnt2;
int cnt3;
cnt1 = cnt / 32;
cnt2 = (cnt - (32*cnt1)) / 4;
cnt3 = (cnt - (32*cnt1) - (4*cnt2));
__asm
{
//; Set up for loop
mov edi, A; // Address of A source1
mov esi, B; // Address of B source2
mov ebx, C; // Address of C dest
mov ecx, cnt1; // Counter
jecxz ZERO;
L1:
movq mm0, [edi]; //Load from A
movq mm1, [edi+8]; //Load from A
movq mm2, [edi+16]; //Load from A
movq mm3, [edi+24]; //Load from A
movq mm4, [edi+32]; //Load from A
movq mm5, [edi+40]; //Load from A
movq mm6, [edi+48]; //Load from A
movq mm7, [edi+56]; //Load from A
pmullw mm0, [esi]; //Load from B & multiply B * (A*C)
pmullw mm1, [esi+8]; //Load from B & multiply B * (A*C)
pmullw mm2, [esi+16]; //Load from B & multiply B * (A*C)
pmullw mm3, [esi+24]; //Load from B & multiply B * (A*C)
pmullw mm4, [esi+32]; //Load from B & multiply B * (A*C)
pmullw mm5, [esi+40]; //Load from B & multiply B * (A*C)
pmullw mm6, [esi+48]; //Load from B & multiply B * (A*C)
pmullw mm7, [esi+56]; //Load from B & multiply B * (A*C)
movq [ebx], mm0; //Store C = A*B
movq [ebx+8], mm1; //Store C = A*B
movq [ebx+16], mm2; //Store C = A*B
movq [ebx+24], mm3; //Store C = A*B
movq [ebx+32], mm4; //Store C = A*B
movq [ebx+40], mm5; //Store C = A*B
movq [ebx+48], mm6; //Store C = A*B
movq [ebx+56], mm7; //Store C = A*B
add edi, 64;
add esi, 64;
add ebx, 64;
loop L1; // Loop if not done
ZERO:
mov ecx, cnt2;
jecxz ZERO1;
L2:
movq mm1, [edi]; //Load from A
pmullw mm1, [esi]; //Load from B & multiply B * (A*C)
movq [ebx], mm1;
add edi, 8;
add esi, 8;
add ebx, 8;
loop L2;
ZERO1:
mov ecx, cnt3;
jecxz ZERO2;
mov eax, 0;
L3: //Really finish off loop with non SIMD instructions
mov eax, [edi];
imul eax, [esi];
mov [ebx], ax;
add esi, 2;
add edi, 2;
add ebx, 2;
loop L3;
ZERO2:
EMMS;
}
}
和我打电话的一个例子。
int A[8] = {-1, 4, 1, -1, 1, -2, -3, 7};
int B[8] = {-1, -1, -1, -1, -1, -1, -1, 1};
int C[8];
mmx_mul(A, B, C, 16);
最后一个参数 16 是 A 和 B 中组合的总元素数。
我正在使用的库是免费使用的,可以在https://www.ngs.noaa.gov/gps-toolbox/Heckler.htm找到