根据下面所示的Go基准测试(通过运行go test -v -bench=. -benchmem),将一个字节的数组附加到一个数组中,每个操作需要1次分配.

Benchmark_AddOneByteArray-4              1986709           581.2 ns/op      1792 B/op          1 allocs/op

但是,附加一个2字节的数组每个操作需要花费101个分配,这与附加一个32字节的数组(101个分配)相同.

Benchmark_AddTwoByteArray
Benchmark_AddTwoByteArray-4               529726          2235 ns/op        1992 B/op        101 allocs/op
Benchmark_AddThirtyTwoByteArray
Benchmark_AddThirtyTwoByteArray-4         282092          4431 ns/op        4992 B/op        101 allocs/op

为什么附加一个字节数组每次操作只需要1次分配,而所有其他测试大小的数组每次操作只需要101次分配?

func addOneByteArray(n int) []any {
    my_array := make([]any, n)

    for i := 0; i < n; i++ {
        my_array[i] = [1]byte{}
    }
    return my_array
}
func addTwoByteArray(n int) []any {
    my_array := make([]any, n)

    for i := 0; i < n; i++ {
        my_array[i] = [2]byte{}
    }
    return my_array
}
func addThirtyTwoByteArray(n int) []any {
    my_array := make([]any, n)

    for i := 0; i < n; i++ {
        my_array[i] = [32]byte{}
    }
    return my_array
}

var N = 100
func Benchmark_AddOneByteArray(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = addOneByteArray(N)
    }
}

func Benchmark_AddTwoByteArray(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = addTwoByteArray(N)
    }
}

func Benchmark_AddThirtyTwoByteArray(b *testing.B) {
    for i := 0; i < b.N; i++ {
        _ = addThirtyTwoByteArray(N)
    }
}

推荐答案

这是对接口的优化,当值适合接口表示时.

通常,接口被表示为具有两个指针的数据块;一个是指向接口元数据的指针,另一个是指向其值的指针. 但是,当值不大于指针时,有一个优化,以适应接口块本身的值.

感谢@ rocka2q和@ coxley提供的线索.

注意,这是golang运行时代码中的优化,而不是编译器中的优化,因此更改编译器优化选项不会有什么不同.

有关更多信息,请参见https://g4s8.wtf/posts/go-low-latency-one/,但请注意,它关于函数参数的描述对于接口变量也是正确的.

Go相关问答推荐

在保留额外参数的同时解封YAML

在不耗尽资源的情况下处理S3文件下载

由docker中的nginx提供的样式和图像在页面上不起作用

Golang String

理解Golang中的IOTA和常量

如何将 goose 迁移与 pgx 一起使用?

如何在golang中使用viper获取对象的配置数组?

如何将 DirName 和 serial 添加到 X509v3 Authority Key Identifier

如何使用 sync.WaitGroup 来执行所有的 goroutine?

golang:解组动态 YAML 注释

使用 Go 解组 SOAP 消息

使用 Grafana alert 在几分钟内重复alert

整理时转换值

仅在工作日运行 cron

Golang Gin 绑定请求正文 XML 到 Slice

Golang模板无法访问embedFS中的文件

使用 AppID 在 Windows 中启动应用程序并获取 pid

Go 导入范围查找 protobuf 类型

将 Simple Go Web 应用程序部署到 Elastic Beanstalk

在 etcd 键值存储中禁用历史记录