代码之家  ›  专栏  ›  技术社区  ›  MPelletier

帮助改进简单的装配功能

  •  5
  • MPelletier  · 技术社区  · 15 年前

    我刚在作业中交了这个函数。完成了(因此没有作业标签)。但我想看看如何改进这一点。

    本质上,该函数使用以下公式对1和给定数字之间的所有整数的平方和进行求和:

    n(n+1)(2n+1)/6
    

    在哪里? n 是最大数目。

    下面的函数用于捕获任何溢出,如果发生则返回0。

    UInt32 sumSquares(const UInt32 number)
    {
        int result = 0;
        __asm
        {
            mov eax, number  //move number in eax
            mov edx, 2       //move 2 in edx
            mul edx          //multiply (2n)
            jo end           //jump to end if overflow
            add eax, 1       //addition (2n+1)
            jo end           //jump to end if overflow
            mov ecx, eax     //move (2n+1) in ecx
    
            mov ebx, number  //move number in ebx
            add ebx, 1       //addition (n+1)
            jo end           //jump to end if overflow
    
            mov eax, number //move number in eax for multiplication
            mul ebx         //multiply n(n+1)
            jo end          //jump to end if overflow
            mul ecx         //multiply n(n+1)(2n+1)
            jo end          //jump to end if overflow
            mov ebx, 6      //move 6 in ebx
            div ebx         //divide by 6, the result will be in eax
    
            mov result, eax //move eax in result
    
    end:
        }
    
        return result;
    }
    

    基本上,我想知道在那里我能改进什么。主要是在最佳实践方面。有一件事听起来很明显:更智能的溢出检查(对于任何最大输入都会导致溢出的情况只进行一次检查)。

    3 回复  |  直到 15 年前
        1
  •  8
  •   Doug Currie    15 年前
        mov eax, number  //move number in eax
        mov ecx, eax     //dup in ecx
        mul ecx          //multiply (n*n)
        jo end           //jump to end if overflow
        add eax, ecx     //addition (n*n+n); can't overflow
        add ecx, ecx     //addition (2n); can't overflow
        add ecx, 1       //addition (2n+1); can't overflow
        mul ecx          //multiply (n*n+n)(2n+1)
        jo end           //jump to end if overflow
        mov ecx, 6       //move 6 in ebx
        div ecx          //divide by 6, the result will be in eax
    
        mov result, eax //move eax in result
    

    强度降低:增加而不是增加。

    通过分析,更少的溢出检查(您可以像您描述的那样做得更好)。

    将值保存在寄存器中,而不是返回堆栈上的参数。

    仔细选择寄存器,这样可以重复使用的值就不会被覆盖。

        2
  •  3
  •   Matthew T. Staebler    15 年前
    mov eax, number    ; = n
    cmp eax, 0x928     ; cannot handle n >= 0x928
    jnc end 
    shl eax, 1         ; = n(2)
    add eax, 3         ; = n(2)+3
    mov ebx, number
    mul ebx            ; = n(n(2)+3)
    add eax, 1         ; = n(n(2)+3)+1
    mov ebx, number
    mul ebx            ; = n(n(n(2)+3)+1) = n(n+1)(2n+1)
    mov ebx, 6
    div ebx
    mov result, eax
    

    此解决方案不是检查溢出,而是根据函数可以处理的已知最大值检查输入。请注意,允许最后一个乘法溢出,并且对于任何输入它都将溢出。 number 大于 0x509 . 对已知值进行检查而不是依赖于溢出检查,这使得函数可以处理几乎是输入值两倍的输入值。实际上,该函数能够处理结果在32位以内的每个输入。

        3
  •  3
  •   Sparafusile    15 年前
    UInt32 sumSquares(const UInt32 number)
    {
      __asm
      {
        mov eax, number     // n
        cmd eax, MAX_VALUE
        jg  bad_value
    
        lea ebx, [eax+1]    // n + 1
        lea ecx, [2*eax+1]  // 2n + 1
    
        mul ebx
        mul ecx
    
        shr eax, 1          // divide by 2
        mov ebx, 2863311531 // inverse of 3
        mul ebx             // divide by 3
    
        ret
    
        bad_value:
        xor eax, eax        // return 0
        ret
      }
    }
    

    http://blogs.msdn.com/devdev/archive/2005/12/12/502980.aspx

    斯巴拉

    推荐文章