2017-05-29 212 views
1

我在裝配sse中遇到了兩個寄存器的乘法問題。 這裏有我的代碼:xmm寄存器的乘法

moltiplicazionePuntoPunto: 
    mov edx,[esp+20]     ; edx = fxx 
    mov esi,[esp+4]     ; esi = fx 
    mov edi,[esp+8]     ; edi = fy 
    xor eax,eax      ; i=0 
fori: cmp eax,[esp+12]   ; confronta i con N 
    jge endfori 
    xor ebx,ebx      ; j=0 
forj: cmp ebx,[esp+16]    ; confronta j con M 
    jge endforj 
    mov ecx,eax 
    imul ecx,[esp+16]     ; ecx = i*M 
    add ecx,ebx      ; ecx = i*M+j 
    movss xmm5,[esi+ecx*4]   ; xmm5 = fx[i*M+j] 
    movss xmm6,[edi+ecx*4]   ; xmm6 = fy[i*M+j] 
    mulps xmm5,xmm6     ; xmm7 = fx[i*M+j]*fx[i*M+j] 
    movss [edx+ecx*4],xmm5   ; fxx[i*M+j] = fx*fx 
    inc ebx 
    jmp forj 
endforj: 
    inc eax 
    jmp fori 
endfori: 

此代碼修改矩陣FXX其中元素爲fxx [I * M + D] = FX [I * M + D] * FY [I * M + D]。問題是當我做的操作mulps xmm5,xmm6結果是0.

+1

如果'mulps xmm5,xmm6'爲零,那麼'xmm5'或'xmm6'中的一個爲零。那它是哪一個?你爲什麼不使用C++,它肯定會產生更快的循環,至少它會優化'i * M'等等,再加上它可能會更容易調試和維護。 – Ped7g

+0

其實當然有其他的角落案例,其中float x * float y = 0,即使當x/y都非爲零時,因爲float本身的精確度有限,所以例如'1e-23 * 1e-23 = 0 '等等......如果沒有來自調試器的一些示例數據,就不可能知道你遇到了什麼,如果你看到這些數據,你可能也會看到答案。 – Ped7g

+0

我認爲這個問題不在於寄存器的值。因爲如果我改變istruction mulps與istruction addps代碼工作 –

回答

0

例如簡化的C++,它只會通過矩陣的所有元素,因爲這就是你的嵌套循環所做的。你並不需要計算i*M+j,爲你的公式不使用I/J,在任何特定的方式,它只是通過所有的元素去一次:

void muldata(float* fxx, const float* fx, const float* fy, const unsigned int M, const unsigned int N) { 
    int ofs = 0; 
    do { 
     fxx[ofs] = fx[ofs] * fy[ofs]; 
     ++ofs; 
    } while (ofs < M*N); 
} 

會讓clang -O3 -m32(V4.0.0)產生這樣的:

muldata(float*, float const*, float const*, unsigned int, unsigned int):     # @muldata(float*, float const*, float const*, unsigned int, unsigned int) 
     push ebp 
     push ebx 
     push edi 
     push esi 
     sub  esp, 12 
     mov  esi, dword ptr [esp + 48] 
     mov  edi, dword ptr [esp + 40] 
     mov  ecx, dword ptr [esp + 36] 
     mov  edx, dword ptr [esp + 32] 
     mov  eax, 1 
     imul esi, dword ptr [esp + 44] 
     cmp  esi, 1 
     cmova eax, esi 
     xor  ebp, ebp 
     cmp  eax, 8 
     jb  .LBB0_7 
     mov  ebx, eax 
     and  ebx, -8 
     je  .LBB0_7 
     mov  dword ptr [esp + 4], eax # 4-byte Spill 
     cmp  esi, 1 
     mov  eax, 1 
     mov  dword ptr [esp], ebx # 4-byte Spill 
     cmova eax, esi 
     lea  ebx, [ecx + 4*eax] 
     lea  edi, [edx + 4*eax] 
     mov  dword ptr [esp + 8], ebx # 4-byte Spill 
     mov  ebx, dword ptr [esp + 40] 
     cmp  edx, dword ptr [esp + 8] # 4-byte Folded Reload 
     lea  eax, [ebx + 4*eax] 
     sbb  bl, bl 
     cmp  ecx, edi 
     sbb  bh, bh 
     and  bh, bl 
     cmp  edx, eax 
     sbb  al, al 
     cmp  dword ptr [esp + 40], edi 
     mov  edi, dword ptr [esp + 40] 
     sbb  ah, ah 
     test bh, 1 
     jne  .LBB0_7 
     and  al, ah 
     and  al, 1 
     jne  .LBB0_7 
     mov  eax, dword ptr [esp] # 4-byte Reload 
     lea  ebx, [edi + 16] 
     lea  ebp, [ecx + 16] 
     lea  edi, [edx + 16] 
.LBB0_5:        # =>This Inner Loop Header: Depth=1 
     movups xmm0, xmmword ptr [ebp - 16] 
     movups xmm2, xmmword ptr [ebx - 16] 
     movups xmm1, xmmword ptr [ebp] 
     movups xmm3, xmmword ptr [ebx] 
     add  ebp, 32 
     add  ebx, 32 
     mulps xmm2, xmm0 
     mulps xmm3, xmm1 
     movups xmmword ptr [edi - 16], xmm2 
     movups xmmword ptr [edi], xmm3 
     add  edi, 32 
     add  eax, -8 
     jne  .LBB0_5 
     mov  eax, dword ptr [esp] # 4-byte Reload 
     mov  edi, dword ptr [esp + 40] 
     cmp  dword ptr [esp + 4], eax # 4-byte Folded Reload 
     mov  ebp, eax 
     je  .LBB0_8 
.LBB0_7:        # =>This Inner Loop Header: Depth=1 
     movss xmm0, dword ptr [ecx + 4*ebp] # xmm0 = mem[0],zero,zero,zero 
     mulss xmm0, dword ptr [edi + 4*ebp] 
     movss dword ptr [edx + 4*ebp], xmm0 
     inc  ebp 
     cmp  ebp, esi 
     jb  .LBB0_7 
.LBB0_8: 
     add  esp, 12 
     pop  esi 
     pop  edi 
     pop  ebx 
     pop  ebp 
     ret 

這是遠遠優於您的代碼(默認情況下包括循環矢量化)。

如果你指定對齊指針並且使M/N編譯時間保持不變,那麼它可能會產生更好的結果。


我只是驗證了C++變種作品將cpp.sh網站,它延伸到這一點:

#include <iostream> 

void muldata(float* fxx, const float* fx, const float* fy, const unsigned int M, const unsigned int N) { 
    unsigned int ofs = 0; 
    do { 
     fxx[ofs] = fx[ofs] * fy[ofs]; 
     ++ofs; 
    } while (ofs < M*N); 
} 

int main() 
{ 
    // constexpr unsigned int M = 1; 
    // constexpr unsigned int N = 1; 
    // const float fx[M*N] = { 2.2f }; 
    // const float fy[M*N] = { 3.3f }; 

    constexpr unsigned int M = 3; 
    constexpr unsigned int N = 2; 
    const float fx[M*N] = { 2.2f, 1.0f, 0.0f, 
          1.0f, 1.0f, 1e-24f }; 
    const float fy[M*N] = { 3.3f, 3.3f, 3.3f, 
          5.5f, 1e30f, 1e-24f }; 

    float fr[M*N]; 
    muldata(fr, fx, fy, M, N); 
    for (unsigned int i = 0; i < N; ++i) { 
     for (unsigned int j = 0; j < M; ++j) std::cout << fr[i*M+j] << " "; 
     std::cout << std::endl; 
    } 
} 

輸出:

7.26 3.3 0 
5.5 1e+30 0 

還有的還評論了1x1的輸入數據,這應該是你的案例中第一個要調試的東西。試着讓這個例子在你最喜歡的C++ IDE中工作,然後用你的彙編代碼替換muldata,並通過它進行調試,看看它發生了什麼。