Skip to content

Commit 617955d

Browse files
authored
【翻译完成】20200605 Go: Stringer Command, Efficiency Through Code Generation (#1837)
* 翻译完成 * 修改图片地址 * 修复mdl lint问题 * 修改校对内容
1 parent 73f3c9d commit 617955d

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# GO: `stringer` 命令,通过代码生成提高效率
2+
![由 Renee French 创作的原始 Go Gopher 作品,为“ Go 的旅程”创作的插图。](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/00.png)
3+
ℹ️ 这篇文章基于 Go 1.13。
4+
5+
`stringer` 命令的目标是自动生成满足 `fmt.Stringer` 接口的方法。它将为指定的类型生成 `String()` 方法, `String()` 返回的字符串用于描述该类型。
6+
7+
## 例子
8+
这个[命令的文档](https://godoc.org/golang.org/x/tools/cmd/stringer) 中给我们提供了一个学习的例子,如下:
9+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/01.png)
10+
输出如下:
11+
```
12+
1
13+
```
14+
产生的日志是一个常量的值,这可能会让你感到困惑。
15+
让我们用命令 `stringer -type=Pill` 生成 `String()` 方法吧:
16+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/02.png)
17+
生成了新的 `String()` 函数,运行当前代码时输出如下:
18+
```
19+
Aspirin
20+
```
21+
现在描述该类型的是一个字符串,而不是它实际的常量值了。
22+
`stringer` 也可以与 `go generate` 命令完美配合,使其功能更强大。只需在代码中添加以下指令即可:
23+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/03.png)
24+
然后,运行 `go generate` 命令将会为你所有的类型自动生成新的函数。
25+
26+
## 效率
27+
`stringer` 生成了一个包含每一个字符串的 ` 长字符串 ` 和一个包含每一个字符串索引的数组。在我们这个例子里,读 `Aspirin` 即是读 ` 长字符串 ` 的索引 7 到 13 组成的字符串:
28+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/04.png)
29+
30+
但是它有多快、多高效?我们来和另外两种方案对比一下:
31+
* 硬编码 `String()` 函数
32+
33+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/05.png)
34+
下面是一个包含 20 个常量的基准测试:
35+
```
36+
name time/op
37+
Stringer-4 4.16ns ± 2%
38+
StringerWithSwitch-4 3.81ns ± 1%
39+
```
40+
包含 100 个常量的基准测试:
41+
```
42+
name time/op
43+
Stringer-4 4.96ns ± 0%
44+
StringerWithSwitch-4 4.99ns ± 1%
45+
```
46+
常量越多,效率越高。这是有道理的。从内存中加载一个值比一些跳转指令(表示 if 条件的汇编指令)更具有膨胀性。
47+
然而,switch 语句分支越多,跳转指令的数量就越多。从某种程度上来说,从内存中加载将会变得更有效。
48+
49+
* `String()` 函数输出一个 map
50+
51+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/06.png)
52+
下面是一个包含 20 个常量的基准测试:
53+
```
54+
name time/op
55+
Stringer-4 4.16ns ± 2%
56+
StringerWithMap-4 28.60ns ± 2%
57+
```
58+
使用 map 要慢得多,因为它必须进行函数调用,并且在 bucket 中查找不像访问切片的索引那么简单。
59+
想了解更多关于 map 的信息和内部结构,我建议你阅读我的文章 "[Go: Map Design by Code](https://medium.com/a-journey-with-go/go-map-design-by-code-part-ii-50d111557c08)"
60+
61+
## 自检器
62+
在生成的代码中,有一些纯粹是为了校验的目的。下面是这些指令:
63+
![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/07.png)
64+
`stringer` 将常量的名称与值一起写入每行。在本例中,`Aspirin` 的值为 `2`。更新常量的名称或其值会产生错误
65+
* 更新名称但不重新生成 `String()` 函数:
66+
```
67+
./pill_string.go:12:8: undefined: Aspirin
68+
```
69+
* 更新值但不重新生成 `String()` 函数:
70+
```
71+
./pill_string.go:12:7: invalid array index Aspirin - 1 (out of bounds for 1-element array)
72+
./pill_string.go:13:7: invalid array index Ibuprofen - 2 (index must be non-negative
73+
```
74+
然而,当我们添加一个新的常量的情况下 -- 这里下一个值为 `3`,并且不更新生成的文件,`stringer` 会有一个默认值:
75+
```
76+
Pill(3)
77+
```
78+
添加这个自检不会有任何影响,因为它在编译时被删除了。可以通过查看程序生成的 asm 代码来确认 :
79+
```
80+
➜ go tool compile -S main.go pill_string.go | grep "\"\".Pill\.[^\s]* STEXT"
81+
"".Pill.String STEXT size=275 args=0x18 locals=0x50
82+
```
83+
只有 `String()` 函数被编译到二进制文件中 , 自检对性能或二进制大小没有影响。
84+
85+
---
86+
via: https://medium.com/a-journey-with-go/go-stringer-command-efficiency-through-code-generation-df49f97f3954
87+
88+
作者:[Vincent Blanchon](https://medium.com/@blanchon.vincent)
89+
译者:[kagxin](https://github.com/kagxin)
90+
校对:[校对者 ID](https://github.com/校对者ID)
91+
92+
本文由 [GCTT](https://github.com/studygolang/GCTT) 原创编译,[Go 中文网](https://studygolang.com/) 荣誉推出

0 commit comments

Comments
 (0)