Go 1.15 中 var i interface{} = a 会有额外堆内存分配吗?——20230122

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
var a  int = 3
// 以下有额外内存分配吗
var i interface{} = a

答案解析

在 Go 中接口被实现为一对指针指向有关类型信息的指针和指向值的指针。可以简单的表示为

type iface struct {
	// 类型
    tab  *itab
    // 值
    data unsafe.Pointer
}

其中 tab 是指向类型信息的指针data 是指向值的指针。因此一般来说接口意味着必须在堆中动态分配该值。

然而Go 1.15 发行说明在 runtime 部分中提到了一个有趣的改进

  • Converting a small integer value into an interface value no longer causes allocation.

意思是说将小整数转换为接口值不再需要进行内存分配。小整数是指 0 到 255 之间的数。

基准测试

函数中进行了 100 次 int 到 interface 的转换.

package smallint

func Convert(val int) []interface{} {
    var slice = make([]interface{}, 100)
    for i := 0; i < 100; i++ {
        slice[i] = val
    }

    return slice
}
---------------------------
package smallint_test

import (
    "testing"
    "test/smallint"
)

func BenchmarkConvert(b *testing.B) {
    for i := 0; i < b.N; i++ {
        result := smallint.Convert(12)
        _ = result
    }
}
  1. go1.14 和 go1.15
$ go version
go version go1.14.7 darwin/amd64
$ go test -bench . -benchmem ./...
goos: darwin
goarch: amd64
pkg: test/smallint
BenchmarkConvert-8      569830       1966 ns/op     2592 B/op      101 allocs/op
PASS
ok   test/smallint 1.647s
$ go version
go version go1.15 darwin/amd64
$ go test -bench . -benchmem ./...
goos: darwin
goarch: amd64
pkg: test/smallint
BenchmarkConvert-8     1859451        655 ns/op     1792 B/op        1 allocs/op
PASS
ok   test/smallint 2.178s
  1. go 1.15但把12改为256
$ go test -bench . -benchmem ./...
goos: darwin
goarch: amd64
pkg: test/smallint
BenchmarkConvert-8      551546       2049 ns/op     2592 B/op      101 allocs/op
PASS
ok   test/smallint 1.502s

证明了上面提到的优化点。

Go 中定义了一个特殊的静态数组该数组由 256 个整数组成0 到 255。当必须分配内存以将整数存储在堆上并将其转换为接口的一部分时它首先检查是否它可以只返回指向数组中适当元素的指针。这种经常使用的值的静态分配是一种很常见的优化手段例如Python 对小整数执行类似的操作。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go