mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
135 lines
2.9 KiB
Markdown
135 lines
2.9 KiB
Markdown
# 字符串优化模式
|
|
|
|
## strconv vs fmt
|
|
|
|
在基本类型和字符串之间转换时,`strconv` 比 `fmt` 更快,因为 `fmt` 使用反射并处理任意类型。
|
|
|
|
**不好:**
|
|
|
|
```go
|
|
for i := 0; i < b.N; i++ {
|
|
s := fmt.Sprint(rand.Int())
|
|
}
|
|
```
|
|
|
|
**好:**
|
|
|
|
```go
|
|
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)` |
|
|
|
|
---
|
|
|
|
## 重复的字符串到字节转换
|
|
|
|
不要重复从固定字符串创建字节切片。应该只转换一次并保存结果。
|
|
|
|
**不好:**
|
|
|
|
```go
|
|
for i := 0; i < b.N; i++ {
|
|
w.Write([]byte("Hello world"))
|
|
}
|
|
```
|
|
|
|
**好:**
|
|
|
|
```go
|
|
data := []byte("Hello world")
|
|
for i := 0; i < b.N; i++ {
|
|
w.Write(data)
|
|
}
|
|
```
|
|
|
|
**基准测试比较:**
|
|
|
|
| 方式 | 速度 |
|
|
|------|------|
|
|
| 重复转换 | 22.2 ns/op |
|
|
| 单次转换 | 3.25 ns/op |
|
|
|
|
好的版本**快约 7 倍**,因为它避免了每次迭代都分配新的字节切片。
|
|
|
|
---
|
|
|
|
## 字符串拼接
|
|
|
|
根据复杂度选择正确的字符串构建策略。
|
|
|
|
### 简单场景使用 `+`
|
|
|
|
```go
|
|
key := "projectid: " + p
|
|
```
|
|
|
|
`+` 运算符对于少量、固定数量的字符串是高效的。编译器通常可以优化相邻的字符串字面量。
|
|
|
|
### 格式化使用 `fmt.Sprintf`
|
|
|
|
```go
|
|
// 好:清晰的格式化
|
|
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` 在构建大字符串时花费二次时间:
|
|
|
|
```go
|
|
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()
|
|
```
|
|
|
|
### 常量多行字符串使用反引号
|
|
|
|
```go
|
|
// 好:原始字符串字面量
|
|
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) |
|
|
| 反引号字面量 | 常量多行文本 | 零开销 |
|