使用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
相关产品推荐
相关产品推荐

