go 的一些琐碎细节记录

1.常量是被精确表示的并且只在编译时期可以被计算

参考:
Methods, Interfaces and Embedded Types in Go
比如可以定义:

// Much larger value than int64.
const (
    myConst  = 9223372036854775808543522345
    myConst2 = myConst / 10000000000000
)

那么 myConst 不能赋值给变量,因为超过了 int64 范围,而myConst2则可以。

2.切片 slice: [i,j,k]

参考:
Ardan Labs Blog Three-Index Slices in Go 1.2 Three-Index Slices in Go 1.2
k-i可以限定新切片的 cap 大小

3.当切片遍历频率高时不一定要使用指针类型数据存放到切片或数组

参考:
Using Pointers In Go
比如:

var bs []BuoyStation

可以让cpu缓存接下来的连续数据,如果使用指针,则只是指针的值在内存中连续,而数据值则是分散的。

4.逃逸分析相关

参考:
Language Mechanics On Escape Analysis
Language Mechanics On Memory Profiling

1) 返回指针逃逸

//go:noinline
func createUserV1() user {
    u := user{
        name:  "Bill",
        email: "bill@ardanlabs.com",
    }

    println("V1", &u)
    return u
}

//go:noinline
func createUserV2() *user {
    u := user{
        name:  "Bill",
        email: "bill@ardanlabs.com",
    }

    println("V2", &u)
    return &u
}

createUserV1 会使 u 被分配在栈上,且有两份副本(调用 createUserV1 的栈帧和 createUserV1 内部的栈帧)
而 createUserV2 的 u 会被分配到堆上,由 GC 管理。

2) interface{} 逃逸

使用 interface{} 作为函数参数也会使得逃逸,

    input := &Buffer{buf: buf}
...
    io.ReadFull(input, buf[:end])

比如 io.ReadFull 使用了 Read 接口,也会产生逃逸。

    input := &Buffer{buf: buf}
...
    input.Read(buf[:end])

改为 input.Read(buf[:end]) 则不存在 interface{} 转换,从而不存在逃逸。
至于为什么传入 interface 会被逃逸,这有个issue https://github.com/golang/go/issues/7213
以及到现在 go1.9 时还存在的编译器 BUG
Escape-Analysis Flaws
其中在当前 1.11 时以下 BUG 已经被解决了:

        var y2 int
        func(p *int, x int) {
            *p = x
        }(&y2, 42) // BAD: Cause of y2 escape, 1.11 is GOOD

3) 对于编译期内存大小不确定的变量也会逃逸

buf := make([]byte, size)

比如对于 make 中传入运行时才知道的变量 size 则会使得编译期无法知道 buf 大小,从而产生逃逸。
改成固定大小后就会被分配在栈上了(前提不能超过最大大小限制)

buf := make([]byte, 5)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

请输入正确的验证码