聊一个string和[]byte转换问题

搜狗商业技术团队
实战 阅读量
前几天闲聊的时候,景埕说网上很多 string 和 []byte 的转换都是有问题的,当时并没有在意,转过身没几天我偶然看到字节跳动的一篇文章,其中提到了他们是如何优化 string 和 []byte 转换的,我便问景埕有没有问题,讨论过程中学到了很多,于是便有了这篇总结。 让我们看看问题代码,类似的 string 和 []byte 转换代码在网上非常常见: func StringToSliceByte(s string) []byte { l := len(s) return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ Data: (*(*reflect.StringHeader)(unsafe.Pointer(&s))).Data, Len: l, Cap: l, })) } 大家之所以不愿意直接通过 []byte(string) 把 string 转换为 []byte,是因为那样会牵扯内存拷贝,而通过 unsafe.Pointer 来做类型转换,没有内存拷贝,从而达到提升性能的目的。 问题代码到底有没有问题?其实当我把代码拷贝到 vscode 之后就有提示了: SliceHeader is the runtime representation of a slice. It cannot be used safely or portably and its representation may change in a later release. Moreover, the Data field is not sufficient to guarantee the data it references will not be garbage collected, so programs must keep a separate, correctly typed pointer to the underlying data.

从一个data race问题学到的

搜狗商业技术团队
实战 阅读量
前几天我在学习内存屏障的时候搜到一篇文章「Golang Memory Model」,其中在介绍 CPU 缓存一致性的时候提到一个例子,带给我一些困惑,本文记录下解惑过程。 既然是在介绍 CPU 缓存一致性的时候举的例子,那么理所应当与此有关,看代码: package main import "time" func main() { running := true go func() { println("start thread1") count := 1 for running { count++ } println("end thread1: count =", count) }() go func() { println("start thread2") for { running = false } }() time.Sleep(time.Hour) } 当我们通过「go run main.go」运行代码的时候,会发现第一个 goroutine 永远不会结束,就好像 running = false 没有生效一样。对此,文章把原因归结为 CPU 缓存一致性中的线程可见性问题,可是我前后看了几遍也没有看出个所以然来。细心的小伙伴不难发现代码存在 data race 问题:多个 goroutine 并发读写 running 变量,不过当我们通过「go run -race main.

为什么会有atomic.LoadInt32

搜狗商业技术团队
实战 阅读量

前些天我们聊了 Golang 内存对齐的话题,后来我突然想到另一个问题:为什么会有 atomic.LoadInt32?可能你觉得思维太跳跃了,容我慢慢道来:首先,有 atomic.LoadInt64 很正常,因为对一个 int64 来说,它的大小是 8 个字节,如果是 32 位平台的话(字长 4 字节),CPU 一次最多操作 4 个字节,需要两次才能拿到全部数据,所以封装一个 atomic.LoadInt64 来实现原子操作;但是,对一个 int32 数据来说,它的大小是 4 字节,不管是 32 位平台(字长 4 字节),还是 64 位平台(字长 8 字节),CPU 应该都可以保证一次操作拿到数据,换句话说,如果读取一个 int32 数据,那么本身就应该是原子的,可是为什么会有 atomic.LoadInt32,这不是脱了裤子放屁么?

浅谈Golang内存对齐

搜狗商业技术团队
实战 阅读量

如果你在 golang spec 里以「alignment」为关键字搜索的话,那么会发现与此相关的内容并不多,只是在结尾介绍 unsafe 包的时候提了一下,不过别忘了字儿越少事儿越大:

Computer architectures may require memory addresses to be aligned; that is, for addresses of a variable to be a multiple of a factor, the variable’s type’s alignment. The function Alignof takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes. For a variable x:

uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0

The following minimal alignment properties are guaranteed:

  • For a variable x of any type: unsafe.Alignof(x) is at least 1.
  • For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x, but at least 1.
  • For a variable x of array type: unsafe.Alignof(x) is the same as the alignment of a variable of the array’s element type.