You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用time.Since()计算平方根时执行时间异常偏低的问题排查

Go自定义平方根函数计时异常问题

我是Go语言新手,正在通过Go Tour学习Go。其中一个挑战是实现平方根计算,给定初始值为1。经过研究推导,我找到了一个更优的初始值——平均迭代次数减少约25-35%,平均误差减少约7-10%,对大数表现更好。

之后我想测试自定义平方根函数计算1到100000的平方根耗时,用了time.Now()和time.Since()分别统计自定义函数和math.Sqrt()的总耗时,但结果异常:自定义函数仅耗时约3ms,math.Sqrt()耗时显示为0。

我不确定是计时代码有误,还是自定义函数实际确实仅需约3ms(不含打印操作),也疑惑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)
}

问题原因及解决办法

1. math.Sqrt()耗时显示为0的核心原因

  • 编译器优化:Go编译器会自动移除无副作用的代码。你调用math.Sqrt()后没有使用返回值,编译器直接把整个计算循环给删除了,相当于没执行任何运算,耗时自然显示为0。
  • 硬件指令加持:math.Sqrt()底层调用CPU硬件平方根指令(如x86的SQRTSS),单次执行速度极快,但10万次不可能耗时为0,本质还是优化导致代码没实际运行。

2. 自定义函数耗时3ms是否合理

自定义函数基于牛顿迭代法,且你优化了初始值大幅减少迭代次数,10万次计算耗时3ms是合理的——Go执行效率高,单次迭代计算量小,这个结果符合预期。

3. 修正计时代码的方法

要避免编译器优化,必须使用函数返回值,比如做无意义的累加操作:

// 修正math.Sqrt()计时
mathTimeStart := time.Now()
var dummy float64
for i := 1; i < size + 1; i++ {
    dummy += math.Sqrt(float64(i)) // 使用返回值,阻止优化
}
elapsedMath := time.Since(mathTimeStart)
fmt.Println("dummy:", dummy) // 打印dummy防止进一步优化

自定义函数计时也可做同样处理:

customTimeStart := time.Now()
var customDummy float64
var iterDummy float64
for i := 1; i < size + 1; i++ {
    z, iter := Sqrt(float64(i))
    customDummy += z
    iterDummy += iter
}
elapsedCustom := time.Since(customTimeStart)
fmt.Println("custom dummy:", customDummy, "iter dummy:", iterDummy)

4. 更严谨的性能测试方案

推荐用Go标准库testing做基准测试,它会自动处理多次迭代、避免优化、统计精准耗时。创建sqrt_test.go文件:

package main

import (
    "math"
    "testing"
)

func BenchmarkCustomSqrt(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Sqrt(float64(i % 100000 + 1))
    }
}

func BenchmarkMathSqrt(b *testing.B) {
    for i := 0; i < b.N; i++ {
        math.Sqrt(float64(i % 100000 + 1))
    }
}

运行go test -bench=.即可得到准确的基准测试结果。

内容的提问来源于stack exchange,提问作者Firefingertips

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 22:45:28