【问题标题】:multiplication of xmm registerxmm 寄存器的乘法
【发布时间】:2017-10-29 22:52:09
【问题描述】:

我在汇编 sse 中的两个寄存器相乘时遇到问题。 这是我的代码:

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+j] = fx[i*M+j] * fy[i*M+j]。问题是当我执行mulps xmm5,xmm6 操作时,结果为0。

【问题讨论】:

  • 如果mulps xmm5,xmm6 为零,则xmm5xmm6 之一为零。那么它是哪一个呢?你为什么不使用例如 C++,它肯定会产生更快的循环,至少它会优化 i*M 等......而且它可能更容易调试和维护。
  • 其实当然还有其他的极端情况,float x * float y = 0,即使x/y都是非零,因为float本身精度有限,比如1e-23 * 1e-23 = 0等等......如果没有来自调试器的一些示例数据,就不可能知道你遇到了什么,如果你看到数据,你可能也看到了答案。
  • 我认为问题在于 mulps 指令而不是寄存器的值。因为如果我用 istruction addps 更改 istruction mulps 代码就可以工作
  • 就像你的 CPU 坏了,mulps 没有做它应该做的,对吧?使用常识,90% 的软件在这种机器上都会失败。问题出在您的代码和/或数据中,而不是指令中,指令正常工作。您没有提供任何特定的测试用例(minimal reproducible example 包括输入数据、预期输出数据和实际输出数据)。我在下面通过 cpp.sh 在线站点的答案尝试了我的 C++,它按预期工作,我将使用完整的示例更新答案。
  • 问题已解决。问题是我从 C 传递了一个 int 矩阵。相反,如果我传递一个浮点矩阵,代码就可以工作。谢谢大家。抱歉,我是组装新手

标签: assembly x86 simd


【解决方案1】:

问题解决了。问题是我从 C 传递了一个 int 矩阵。相反,如果我传递一个浮点矩阵,代码就可以工作。

【讨论】:

    【解决方案2】:

    例如简化的 C++,它只会遍历矩阵的所有元素,因为这就是您的 [i,j] 嵌套循环所做的。您不需要计算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 编译时间恒定,它可能会产生更好的结果。


    我刚刚通过访问 cpp.sh 站点并将其扩展为以下内容验证了 C++ 变体的工作原理:

    #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,并通过它进行调试,看看它在哪里出错。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-14
      • 2012-04-27
      相关资源
      最近更新 更多