Go: 字符串和转换优化

:information_source: 本文基于Go1.14。

在Go中,将字节数组转换为字符串可能会涉及到内存分配以及转换后的字符串的副本。但是,仅将字节转换为字符串以满足代码约束(例如switch语句中的比较或作为map的key)绝对是浪费CPU时间。让我们回顾一些案例并进行优化。

转换

从字节数组转换为字符串涉及:

  • 如果变量超过当前栈帧,则在堆上分配新字符串。

  • 转换后的字符串的副本。

这是完成这两个步骤的示例程序:

这是转换的图示:

在运行时,Go在转换期间仅提供一种优化。如果被转换的字节数组实际上只包含一个字节,则返回的字符串将指向运行时嵌入的静态字节数组:

但是,如果以后修改此字符串,它将在分配新值之前从堆中分配内存。

Go编译器还提供了一些优化功能,可以跳过我们看到的转换的两个阶段。

Switch

让我们看一个用于比较意图的转换示例:

这个例子用于说明字符串优化,将会通过 getBytes 函数强制在堆上分配。它避免了其他一些可能隐藏此处介绍的字符串优化的编译器优化。

在此示例中,转换仅用于 switch 比较,而Go可以避免转换,因为它只需要比较实际内容即可。Go实际上通过删除转换并直接指向背后的字节数组来优化代码:

我们还可以看到生成的程序汇编的确切优化:

Go直接在比较中使用返回的字节。它首先检查字节数组和case中字符串的长度然后再检查字符串本身。在 switch 之外进行字符串赋值将导致内存分配,因为编译器将不知道稍后在何处使用该字符串。

其他优化

switch 中并不是唯一适用于转换优化的情况。Go编译器同时将此行为应用于其他情况,例如:

  • 访问map元素。这是一个例子:

访问map时,实际上不需要进行任何转换即可加快访问速度。

  • 字符串比较。这是一些例子:

这种情况和 switch 类似。它首先比较字符串和字节数组的长度,然后对比字符串。

编译整理自 Go: String & Conversion Optimization

https://medium.com/a-journey-with-go/go-string-conversion-optimization-767b019b75ef