问题:使用 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
:
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!