Files
OpenFlare/.agents/skills/go-performance/references/STRING-OPTIMIZATION.md
T
2026-08-04 11:40:32 +08:00

2.9 KiB

字符串优化模式

strconv vs fmt

在基本类型和字符串之间转换时,strconv 比 fmt 更快,因为 fmt 使用反射并处理任意类型。

不好:

for i := 0; i < b.N; i++ {
    s := fmt.Sprint(rand.Int())
}

好:

for i := 0; i < b.N; i++ {
    s := strconv.Itoa(rand.Int())
}

基准测试比较:

方式 速度 分配次数
fmt.Sprint 143 ns/op 2 allocs/op
strconv.Itoa 64.2 ns/op 1 allocs/op

常用转换:

任务 fmt strconv
Int → string fmt.Sprint(n) strconv.Itoa(n)
Int64 → string fmt.Sprint(n) strconv.FormatInt(n, 10)
Float → string fmt.Sprint(f) strconv.FormatFloat(f, 'f', -1, 64)
String → int — strconv.Atoi(s)
Bool → string fmt.Sprint(b) strconv.FormatBool(b)

重复的字符串到字节转换

不要重复从固定字符串创建字节切片。应该只转换一次并保存结果。

不好:

for i := 0; i < b.N; i++ {
    w.Write([]byte("Hello world"))
}

好:

data := []byte("Hello world")
for i := 0; i < b.N; i++ {
    w.Write(data)
}

基准测试比较:

方式 速度
重复转换 22.2 ns/op
单次转换 3.25 ns/op

好的版本快约 7 倍,因为它避免了每次迭代都分配新的字节切片。


字符串拼接

根据复杂度选择正确的字符串构建策略。

简单场景使用 +

key := "projectid: " + p

+ 运算符对于少量、固定数量的字符串是高效的。编译器通常可以优化相邻的字符串字面量。

格式化使用 fmt.Sprintf

// 好:清晰的格式化
str := fmt.Sprintf("%s [%s:%d]-> %s", src, qos, mtu, dst)

// 不好:使用 + 手动转换
str := src.String() + " [" + qos.String() + ":" + strconv.Itoa(mtu) + "]-> " + dst.String()

当写入 io.Writer 时,直接使用 fmt.Fprintf 而不是先用 fmt.Sprintf 构建临时字符串。

逐段构建使用 strings.Builder

strings.Builder 花费摊销线性时间,而重复使用 + 或 fmt.Sprintf 在构建大字符串时花费二次时间:

b := new(strings.Builder)
for i, d := range digitsOfPi {
    fmt.Fprintf(b, "the %d digit of pi is: %d\n", i, d)
}
str := b.String()

常量多行字符串使用反引号

// 好:原始字符串字面量
usage := `Usage:

custom_tool [args]`

// 不好:使用转义序列拼接
usage := "" +
    "Usage:\n" +
    "\n" +
    "custom_tool [args]"

策略总结

方法 最佳用途 性能
+ 少量字符串,简单拼接 小 n 时 O(n)
fmt.Sprintf 格式化输出 较慢,但更清晰
strings.Builder 循环/逐段构建 摊销 O(n)
strings.Join 连接 slice O(n)
反引号字面量 常量多行文本 零开销