Maison > développement back-end > Golang > L'exécution mathématique de Golang est lente lors de l'utilisation du code

L'exécution mathématique de Golang est lente lors de l'utilisation du code

王林
Libérer: 2024-02-05 21:39:03
avant
986 Les gens l'ont consulté

使用代码时 Golang 数学执行缓慢

问题内容

问题:使用 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)
}
Copier après la connexion


正确答案


检查 math.Sqrt 的生成代码。使用 -S 标志构建此 main.go

package main

import (
    "fmt"
    "math"
    "math/rand"
)

func main() {
    x := rand.Float64()
    y := math.Sqrt(x)
    fmt.Println(y)
}
Copier après la connexion

生成程序集: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&#160;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
...
Copier après la connexion

可以看到,实际调用的是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
Copier après la connexion

4609047870845172685 是 64 位表示形式的实际 sqrt(2)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal