使用 time.Since() 计算平方根的执行时间异常短?

问题描述 投票:0回答:1

我是围棋新手,我正在使用 Go Tour 学习围棋。挑战之一是求一个数的平方根。给出了一个公式,初始值为 1。

摆弄了很长一段时间后,我做了一些数学计算,找到了一个更好的初始值(平均迭代次数减少了约 25-35%,平均误差减少了约 7-10%)。这个首字母看起来数字越大越好。

接下来我想做的是检查我的函数计算 1 到 100,000 的平方根需要多长时间。只是为了好玩。

查看 Go 的 time 包,我使用

time.Now()
time.Since()
函数来计算函数的总时间和
math.Sqrt()
总时间。但奇怪的是,这些数字非常非常低(我的函数约为 3 毫秒,0
math.Sqrt()
)。

我不确定我的计时代码是否不正确,如果我的函数实际上只需要〜3毫秒(不包括打印输出),为什么

math.Sqrt()
需要0时间? (它是否可能已经计算出来并且只是在查找?),也许正在发生某种线程操作?

我不知道。

这是我所有的代码

package main

import (
    "fmt"
    "math"
    "strconv"
    "time"
)

func Sqrt(x float64) (float64, float64) {
    count := 0
    initial := 1
    // initial = 1<<2^(digit count)
    for n := x;n > 1; {
        n = n / 10
        initial = initial<<2
        count++
    }

    z := x / float64(initial)
    var p float64
    i := 0.0
    for ; math.Abs(z-p) > 0.0000000001; i = i + 1 {
        p = z
        z -= (z*z - x) / (2 * z)
    }

    return z, i
}

func compute(x int) (float64, float64) {

    input := float64(x)
    guess, iterations := Sqrt(input)
    expected := math.Sqrt(input)
    err := float64(math.Abs(guess-expected))
    fmt.Printf("Sqrt: %v\nExpected: %v\nGuess: %v\nError: %v\nIterations: %v\n\n", input, expected, guess, err, iterations)
    return iterations, err
}

func main() {
    size := 100000
    var totalIterations []int
    var totalErrs []string
    var iterSum float64
    var errSum float64

    
    for i := 1; i < size + 1; i++ {
        iter, err := compute(i)
        totalIterations = append(totalIterations, int(iter))
        formatedErr := strconv.FormatFloat(err, 'E', -1, 64)
        totalErrs = append(totalErrs, formatedErr)
        iterSum += iter
        errSum += err
    }

    fmt.Printf("Iterations sum %v\nAverage iteration count: %v\nError sum: %v\nError average: %v\n", iterSum, iterSum/float64(len(totalIterations)), errSum, errSum/float64(len(totalErrs)))

    // re-running the loops for finding time (temporary), I will incoperate it to the above loop later
    customTimeStart := time.Now()
    for i := 1; i < size + 1; i++ {
        Sqrt(float64(i))
    }
    elapsedCustom := time.Since(customTimeStart)

    mathTimeStart := time.Now()
    for i := 1; i < size + 1; i++ {
        math.Sqrt(float64(i))
    }
    elapsedMath := time.Since(mathTimeStart)
    
    fmt.Printf("Total custom time: %s\nTotal math time: %s\n", elapsedCustom, elapsedMath)
}
go math time math.sqrt
1个回答
0
投票

Golang 编译器内联

math.Sqrt
函数并调用内部函数
math.sqrt

然后在大多数硬件平台上,编译器将

math.sqrt
替换为 SQRT 操作码。在AMD64平台上它是
SQRTSD
汇编指令。请参阅此处了解更多详情。

难怪调用

math.Sqrt
需要纳秒。

自己尝试一下。将你的程序编译成汇编

go build -gcflags=-S some.go

然后检查组件的

compute
功能:

        0x001a 00026 (/mnt/d/tmp/try-go/trygo/some.go:33)       XORPS   X0, X0
        0x001d 00029 (/mnt/d/tmp/try-go/trygo/some.go:33)       CVTSQ2SD        AX, X0
        0x0022 00034 (/mnt/d/tmp/try-go/trygo/some.go:33)       MOVSD   X0, main.input+72(SP)
        0x0028 00040 (/mnt/d/tmp/try-go/trygo/some.go:34)       PCDATA  $1, $0
        0x0028 00040 (/mnt/d/tmp/try-go/trygo/some.go:34)       CALL    main.Sqrt(SB)
        0x002d 00045 (/mnt/d/tmp/try-go/trygo/some.go:34)       MOVSD   X1, main.iterations+64(SP)
        0x0033 00051 (/usr/local/go/src/math/sqrt.go:94)        MOVSD   main.input+72(SP), X2
        0x0039 00057 (/usr/local/go/src/math/sqrt.go:94)        SQRTSD  X2, X3
        0x003d 00061 (/mnt/d/tmp/try-go/trygo/some.go:36)       MOVUPS  X0, X4
        0x0040 00064 (/mnt/d/tmp/try-go/trygo/some.go:36)       SUBSD   X3, X0

看到这行

0x0039 00057 (/usr/local/go/src/math/sqrt.go:94)        SQRTSD  X2, X3
了吗?这是 Go 编译器为
math.Sqrt
调用生成的内容。

© www.soinside.com 2019 - 2024. All rights reserved.