
问题:使用 math 包中的代码比使用 math 包慢得多。
下面的代码是从数学模块复制的,但是 math.Sqrt 的执行速度比 sqrt 快得多。我的问题是:为什么,可以做什么?
示例:(注意,感兴趣的函数不是 math.Sqrt。我想编写函数组合的自定义版本。)
package main
import (
"fmt"
"math"
"unsafe"
)
const (
uvnan = 0x7FF8000000000001
uvinf = 0x7FF0000000000000
uvneginf = 0xFFF0000000000000
uvone = 0x3FF0000000000000
mask = 0x7FF
shift = 64 - 11 - 1
bias = 1023
signMask = 1 << 63
fracMask = 1<<shift - 1
)
func Float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) }
func Float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) }
func sqrt(x float64) float64 {
// special cases
ix := Float64bits(x)
// normalize x
exp := int((ix >> shift) & mask)
if exp == 0 { // subnormal x
for ix&(1<<shift) == 0 {
ix <<= 1
exp--
}
exp++
}
exp -= bias // unbias exponent
ix &^= mask << shift
ix |= 1 << shift
if exp&1 == 1 { // odd exp, double x to make it even
ix <<= 1
}
exp >>= 1 // exp = exp/2, exponent of square root
// generate sqrt(x) bit by bit
ix <<= 1
var q, s uint64 // q = sqrt(x)
r := uint64(1 << (shift + 1)) // r = moving bit from MSB to LSB
for r != 0 {
t := s + r
if t <= ix {
s = t + r
ix -= t
q += r
}
ix <<= 1
r >>= 1
}
// final rounding
if ix != 0 { // remainder, result not exact
q += q & 1 // round according to extra bit
}
ix = q>>1 + uint64(exp-1+bias)<<shift // significand + biased exponent
return Float64frombits(ix)
}
func main() {
n_iter := 100000000
var x float64
var y float64
for i := 0; i < n_iter; i++ {
y = math.Sqrt(2426.1)
}
fmt.Printf("Done\n") // Much, much faster
for i := 0; i < n_iter; i++ {
x = sqrt(2426.1)
}
fmt.Printf("Done\n")
fmt.Printf("%f/%f\n", x, y)
}
检查 math.Sqrt 的生成代码。使用 -S 标志构建此 main.go:
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"fmt"
"math"
"math/rand"
)
func main() {
x := rand.Float64()
y := math.Sqrt(x)
fmt.Println(y)
}生成程序集:go build -gcflags='-S' main.go
...
0x000e 00014 (./main.go:10) PCDATA $1, $0
0x000e 00014 (./main.go:10) CALL math/rand.Float64(SB)
0x0013 00019 (/home/user/go/pkg/mod/golang.org/<a href="https://www.php.cn/link/89fee0513b6668e555959f5dc23238e9" class="__cf_email__" data-cfemail="8afee5e5e6e9e2ebe3e4cafcbaa4baa4bba7ede5bba4b8bba4bfa4e6e3e4fff2a7ebe7eebcbe">[email protected]</a>/src/math/sqrt.go:94) SQRTSD X0, X0
0x0017 00023 (./main.go:12) MOVQ X0, AX
0x001c 00028 (./main.go:11) XCHGL AX, AX
...可以看到,实际调用的是SQRTSD机器指令。
它受 cmd/compile/internal/ssagen/ssa.go 配置:在平台 sys.I386、sys.AMD64、sys.ARM、sys.ARM64、sys.Loong64、sys.MIPS、sys. MIPS64、sys.PPC64、sys.RISCV64、sys.S390X、sys.Wasm 编译器插入 SQRT 操作码。
您还可以检查许多其他被汇编替换的函数。
顺便说一句。在示例中,我在随机参数上调用math.Sqrt ,因为对于文字值,编译器会计算平方根本身并编译为实际值。
这里是math.Sqrt(2.0)的汇编代码
0x0014 00020 (./main.go:11) MOVQ $4609047870845172685, AX 0x001e 00030 (./main.go:11) PCDATA $1, $1 0x001e 00030 (./main.go:11) NOP 0x0020 00032 (./main.go:11) CALL runtime.convT64(SB) 0x0025 00037 (./main.go:11) LEAQ type:float64(SB), CX
值 4609047870845172685 是 64 位表示形式的实际 sqrt(2)。
以上就是使用代码时 Golang 数学执行缓慢的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号