npx skills add https://github.com/cxuu/golang-skills --skill go-performance来源 : Uber Go 风格指南
性能相关的指导原则仅适用于热点路径。不要过早优化——将这些模式集中在最重要的地方。
在基本类型与字符串之间进行转换时,strconv 比 fmt 更快。
来源 : Uber Go 风格指南
不佳示例:
for i := 0; i < b.N; i++ {
s := fmt.Sprint(rand.Int())
}
良好示例:
for i := 0; i < b.N; i++ {
s := strconv.Itoa(rand.Int())
}
基准测试对比:
| 方法 | 速度 |
|---|
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 内存分配次数 |
|---|
fmt.Sprint | 143 ns/op | 2 allocs/op |
strconv.Itoa | 64.2 ns/op | 1 allocs/op |
不要重复地从固定字符串创建字节切片。相反,执行一次转换并捕获结果。
来源 : Uber Go 风格指南
不佳示例:
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 倍,因为它避免了在每次迭代中分配新的字节切片。
在可能的情况下指定容器容量以预先分配内存。这可以最大程度地减少随着元素添加而导致的复制和调整大小所带来的后续分配。
来源 : Uber Go 风格指南
使用 make() 初始化 map 时提供容量提示。
make(map[T1]T2, hint)
注意 : 与切片不同,map 的容量提示并不保证完全预先分配——它近似于所需的哈希表桶数量。
不佳示例:
files, _ := os.ReadDir("./files")
m := make(map[string]os.DirEntry)
for _, f := range files {
m[f.Name()] = f
}
// Map 动态调整大小,导致多次分配
良好示例:
files, _ := os.ReadDir("./files")
m := make(map[string]os.DirEntry, len(files))
for _, f := range files {
m[f.Name()] = f
}
// Map 在初始化时大小合适,分配次数更少
使用 make() 初始化切片时提供容量提示,特别是在进行追加操作时。
make([]T, length, capacity)
与 map 不同,切片的容量不是提示——编译器会精确分配那么多内存。后续的 append() 操作在达到容量之前不会产生任何分配。
不佳示例:
for n := 0; n < b.N; n++ {
data := make([]int, 0)
for k := 0; k < size; k++ {
data = append(data, k)
}
}
良好示例:
for n := 0; n < b.N; n++ {
data := make([]int, 0, size)
for k := 0; k < size; k++ {
data = append(data, k)
}
}
基准测试对比:
| 方法 | 时间 (1亿次迭代) |
|---|---|
| 无容量 | 2.48s |
| 有容量 | 0.21s |
良好版本快约 12 倍,因为在追加过程中没有重新分配。
来源 : Go Wiki CodeReviewComments (建议)
不要仅仅为了节省几个字节而将指针作为函数参数传递。如果一个函数在整个过程中仅将其实参 x 引用为 *x,那么该参数不应是指针。
应直接传递值的常见情况:
*string) —— 字符串已经是小的固定大小的头部*io.Reader) —— 接口是固定大小的(类型 + 数据指针)不佳示例:
func process(s *string) {
fmt.Println(*s) // 仅解引用,从不修改
}
良好示例:
func process(s string) {
fmt.Println(s)
}
例外情况:
| 模式 | 不佳 | 良好 | 改进 |
|---|---|---|---|
| 整数转字符串 | fmt.Sprint(n) | strconv.Itoa(n) | 快约 2 倍 |
重复 []byte | 在循环中使用 []byte("str") | 在外部转换一次 | 快约 7 倍 |
| Map 初始化 | make(map[K]V) | make(map[K]V, size) | 分配次数更少 |
| Slice 初始化 | make([]T, 0) | make([]T, 0, cap) | 快约 12 倍 |
| 小型固定大小参数 | *string, *io.Reader | string, io.Reader | 无间接引用 |
go-style-corego-naming每周安装量
137
代码仓库
GitHub Stars
34
首次出现
Jan 27, 2026
安全审计
安装于
github-copilot130
gemini-cli129
codex129
kimi-cli128
amp128
opencode128
Source : Uber Go Style Guide
Performance-specific guidelines apply only to the hot path. Don't prematurely optimize—focus these patterns where they matter most.
When converting primitives to/from strings, strconv is faster than fmt.
Source : Uber Go Style Guide
Bad:
for i := 0; i < b.N; i++ {
s := fmt.Sprint(rand.Int())
}
Good:
for i := 0; i < b.N; i++ {
s := strconv.Itoa(rand.Int())
}
Benchmark comparison:
| Approach | Speed | Allocations |
|---|---|---|
fmt.Sprint | 143 ns/op | 2 allocs/op |
strconv.Itoa | 64.2 ns/op | 1 allocs/op |
Do not create byte slices from a fixed string repeatedly. Instead, perform the conversion once and capture the result.
Source : Uber Go Style Guide
Bad:
for i := 0; i < b.N; i++ {
w.Write([]byte("Hello world"))
}
Good:
data := []byte("Hello world")
for i := 0; i < b.N; i++ {
w.Write(data)
}
Benchmark comparison:
| Approach | Speed |
|---|---|
| Repeated conversion | 22.2 ns/op |
| Single conversion | 3.25 ns/op |
The good version is ~7x faster because it avoids allocating a new byte slice on each iteration.
Specify container capacity where possible to allocate memory up front. This minimizes subsequent allocations from copying and resizing as elements are added.
Source : Uber Go Style Guide
Provide capacity hints when initializing maps with make().
make(map[T1]T2, hint)
Note : Unlike slices, map capacity hints do not guarantee complete preemptive allocation—they approximate the number of hashmap buckets required.
Bad:
files, _ := os.ReadDir("./files")
m := make(map[string]os.DirEntry)
for _, f := range files {
m[f.Name()] = f
}
// Map resizes dynamically, causing multiple allocations
Good:
files, _ := os.ReadDir("./files")
m := make(map[string]os.DirEntry, len(files))
for _, f := range files {
m[f.Name()] = f
}
// Map is right-sized at initialization, fewer allocations
Provide capacity hints when initializing slices with make(), particularly when appending.
make([]T, length, capacity)
Unlike maps, slice capacity is not a hint —the compiler allocates exactly that much memory. Subsequent append() operations incur zero allocations until capacity is reached.
Bad:
for n := 0; n < b.N; n++ {
data := make([]int, 0)
for k := 0; k < size; k++ {
data = append(data, k)
}
}
Good:
for n := 0; n < b.N; n++ {
data := make([]int, 0, size)
for k := 0; k < size; k++ {
data = append(data, k)
}
}
Benchmark comparison:
| Approach | Time (100M iterations) |
|---|---|
| No capacity | 2.48s |
| With capacity | 0.21s |
The good version is ~12x faster due to zero reallocations during append.
Source : Go Wiki CodeReviewComments (Advisory)
Don't pass pointers as function arguments just to save a few bytes. If a function refers to its argument x only as *x throughout, then the argument shouldn't be a pointer.
Common instances where values should be passed directly:
*string) — strings are already small fixed-size headers*io.Reader) — interfaces are fixed-size (type + data pointers)Bad:
func process(s *string) {
fmt.Println(*s) // only dereferences, never modifies
}
Good:
func process(s string) {
fmt.Println(s)
}
Exceptions:
| Pattern | Bad | Good | Improvement |
|---|---|---|---|
| Int to string | fmt.Sprint(n) | strconv.Itoa(n) | ~2x faster |
Repeated []byte | []byte("str") in loop | Convert once outside | ~7x faster |
| Map initialization | make(map[K]V) | make(map[K]V, size) |
go-style-corego-namingWeekly Installs
137
Repository
GitHub Stars
34
First Seen
Jan 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot130
gemini-cli129
codex129
kimi-cli128
amp128
opencode128
GSAP时间轴动画教程:创建多步骤序列动画与关键帧控制
1,700 周安装
| Fewer allocs |
| Slice initialization | make([]T, 0) | make([]T, 0, cap) | ~12x faster |
| Small fixed-size args | *string, *io.Reader | string, io.Reader | No indirection |