Golang中由编译器直接实现的math/bits函数解析

Golang中由编译器直接实现的math/bits函数解析 math/bits 标准库文档中写道:

此包中的函数可能由编译器直接实现,以获得更好的性能。对于这些函数,将不会使用此包中的代码。哪些函数由编译器实现取决于架构和 Go 发行版。

有谁知道在哪里可以找到针对不同 CPU 架构,哪些函数是由编译器实现的吗?我曾经非常困惑,当时我正在对某些函数进行基准测试,试图为特定的定点类型实现寻找理论上更快的版本,结果却发现编译器已经高效地利用了底层硬件。在我复制标准库代码并自行编译后,才注意到这一点,这给出了预期的(较慢的)基准测试结果。

值得注意的此类函数示例是 bits.Mul64bits.Div64,它们在 x86-64 架构上似乎会编译为支持直接对两个 64 位数字进行乘法或除法运算并处理其 128 位结果的指令。

我很欣赏这本质上是一种硬件加速,但我也希望通过参考一份确认此支持的文档来依赖这种优化。

有人能帮忙吗?


更多关于Golang中由编译器直接实现的math/bits函数解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

完整列表位于 go/src/cmd/compile/internal/ssagen/intrinsics.go at 989eed28497cde7145958985f50bb3dd6ab698b6 · golang/go · GitHub,你需要的那个神奇的编译器设计术语是「内联函数」。这是所有类型编译器中的常见做法。

这个文件可能难以阅读,你主要需要寻找对 addF 闭包的调用。它的第一个字符串参数是包名,第二个是函数名,最后一个是一个回调函数,该回调负责将函数调用转换为一个SSA操作(一种伪汇编,之后可以翻译到任何架构,对编写编译器很有帮助)。

最后,内联函数并非实现此功能的唯一方式。有些其他指令是通过匹配特定的实现模式来实现的,例如,GOAMD64=v3 下,有一条指令原生实现了 func IsPowerOfTwo(uint) bool,但你不会在 math/bits 包中找到它。 相反,优化器会非常精确地寻找 x&(x-1) == 0(或者对于否定情况是 != 0)这种模式。理论上,这个函数还有许多其他实现方式可以达到同样的速度,但实践中只有这种模式会被以那种方式优化。

更多关于Golang中由编译器直接实现的math/bits函数解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,math/bits包的部分函数确实由编译器直接映射到硬件指令,这是性能优化的关键。目前,Go项目并没有提供一份官方文档来详细列出每个架构下哪些函数被编译器直接实现。不过,你可以通过以下途径来确认或探索这些信息:

  1. 查看Go编译器源码:这是最直接的方式。在Go源码树的src/cmd/compile/internal/ssa/gen/*.rules文件中,定义了架构相关的优化规则。例如,在x86架构的规则文件(如x86.rules)中,你可以搜索Mul64Div64等函数名,找到类似以下的规则,这表明编译器会将其转换为硬件指令:

    // 示例:x86.rules中可能存在的规则片段
    (Mul64 x y) -> (MULQ x y)  // 对应x86-64的MULQ指令
    (Div64 ...) -> (DIVQ ...)  // 对应x86-64的DIVQ指令
    
  2. 检查汇编输出:使用go tool compile -Sgo build -gcflags=-S来生成汇编代码,直接查看函数是否被编译为特定指令。例如,对于bits.Mul64

    echo 'package main; import "math/bits"; func f() { _ = bits.Mul64(123, 456) }' | go tool compile -S -
    

    在x86-64输出中,你可能会看到MULQ指令,而不是函数调用。

  3. 参考架构手册:了解CPU架构本身的支持情况。例如,x86-64的MULQDIVQ指令可以直接处理64位乘除法并产生128位结果,这与bits.Mul64bits.Div64的语义匹配。类似地,ARM64等架构也有对应指令。

  4. 社区讨论和代码注释:Go项目的Issue跟踪器和邮件列表有时会讨论这些优化。例如,在源码中,math/bits包的注释或相关commit历史可能提到硬件支持。

以下是一个简单示例,展示如何通过汇编验证bits.Mul64在x86-64上的实现:

package main

import (
    "fmt"
    "math/bits"
)

func main() {
    a, b := uint64(123456789), uint64(987654321)
    hi, lo := bits.Mul64(a, b)
    fmt.Printf("Mul64: hi=%d, lo=%d\n", hi, lo)
}

使用go build -gcflags=-S main.go 2>&1 | grep -A5 -B5 'MULQ',如果输出中包含MULQ指令,则确认是编译器直接实现的。

总之,虽然缺乏集中文档,但通过编译器源码和汇编输出,你可以可靠地确定特定函数在目标架构上是否由硬件指令实现。对于性能关键代码,建议直接验证汇编输出,以确保优化符合预期。

回到顶部