From 6fb2c8b65aef4dae2c1b142dd5add3c8b2d59876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E9=91=AB?= Date: Sat, 14 Nov 2020 21:24:46 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...mand-Efficiency-Through-Code-Generation.md | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md diff --git a/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md b/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md new file mode 100644 index 000000000..75d917c4b --- /dev/null +++ b/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md @@ -0,0 +1,93 @@ +# GO:`stringer`命令,通过代码生成提高效率 +![由Renee French创作的原始Go Gopher作品,为“ Go的旅程”创作的插图。](https://miro.medium.com/max/1400/1*lLJ2BlmnulX1WZGfUTt0vg.png) +ℹ️ 这篇文章基于 Go 1.13。 + +`stringer`命令的目标是自动生成满足`fmt.Stringer`接口的方法。它将为指定的类型生成`String()`方法,`String()`返回的字符串用于描述该类型。 + +## 例子 +这个[命令的文档](https://godoc.org/golang.org/x/tools/cmd/stringer)中给我们提供了一个学习的例子,如下: +![](https://miro.medium.com/max/1400/1*vucGVFa1Qju4pXxzRjiZ9w.png) +输出如下: +``` +1 +``` +产生的日志是一个常量的值,这可能会让你感到困惑。 +让我们用命令`stringer -type=Pill`生成`String()`方法吧: +![](https://miro.medium.com/max/1400/1*i-v4hKZD3vTMz-nxJaKj1A.png) +生成了新的`String()`函数,运行当前代码时输出如下: +``` +Aspirin +``` +现在描述该类型的是一个字符串,而不是它实际的常量值了。 +`stringer`也可以与`go generate`命令完美配合,使其功能更强大。只需在代码中添加以下指令即可: +![](https://miro.medium.com/max/1400/1*BnPEnsqwZLxt9FFR-Jxm9g.png) +然后,运行`go generate`命令将会为你所有的类型自动生成新的函数。 + +## 效率 +`stringer`生成了一个包含每一个字符串的`长字符串`和一个包含每一个字符串索引的数组。在我们这个例子里,读`Aspirin`即是读`长字符串`的索引7到13组成的字符串: +![](https://miro.medium.com/max/1400/1*TJs010AERv1mWUomMsu7qg.png) + +但是它有多快、多高效?我们来和另外两种方案对比一下: +* 硬编码`String()`函数 + +![](https://miro.medium.com/max/1400/1*8HCJlcw_fP24lpooVhJT8g.png) +下面是一个包含20个常量的基准测试: +``` +name time/op +Stringer-4 4.16ns ± 2% +StringerWithSwitch-4 3.81ns ± 1% +``` +包含100个常量的基准测试: +``` +name time/op +Stringer-4 4.96ns ± 0% +StringerWithSwitch-4 4.99ns ± 1% +``` +常量越多,效率越高。这是有道理的。从内存中加载一个值比一些跳转指令(表示if条件的汇编指令)更具有膨胀性。 +然而,当switch语句越大,跳转指令的数量就越多。从某种程度上来说,从内存中加载将会变得更有效。 + +* `String()`函数输出一个map + +![](https://miro.medium.com/max/1400/1*B7JjenIX_IIgWUL5Vf27XQ.png) +下面是一个包含20个常量的基准测试: +``` +name time/op +Stringer-4 4.16ns ± 2% +StringerWithMap-4 28.60ns ± 2% +``` +使用map要慢得多,因为它必须进行函数调用,并且在bucket中查找不像访问切片的索引那么简单。 +想了解更多关于map的信息和内部结构,我建议你阅读我的文章"[Go: Map Design by Code](https://medium.com/a-journey-with-go/go-map-design-by-code-part-ii-50d111557c08)" + +## 自检器 +在生成的代码中,有一些纯粹是为了校验的目的。下面是这些指令: +![](https://miro.medium.com/max/1400/1*_LaRSP60WqMzA3Lz9AhkcA.png) +`stringer`将常量的名称与值一起写入每行。在本例中,`Aspirin`的值为`2`。更新常量的名称或其值会产生错误 +* 更新名称但不重新生成`String()`函数: +``` +./pill_string.go:12:8: undefined: Aspirin +``` +* 更新值但不重新生成`String()`函数: +``` +./pill_string.go:12:7: invalid array index Aspirin - 1 (out of bounds for 1-element array) +./pill_string.go:13:7: invalid array index Ibuprofen - 2 (index must be non-negative +``` +然而,当我们添加一个新的常量的情况下--这里下一个值为`3`,并且不更新生成的文件,`stringer`会有一个默认值: +``` +Pill(3) +``` +添加这个自检不会有任何影响,因为它在编译时被删除了。可以通过查看程序生成的asm代码来确认: +``` +➜ go tool compile -S main.go pill_string.go | grep "\"\".Pill\.[^\s]* STEXT" +"".Pill.String STEXT size=275 args=0x18 locals=0x50 +``` +只有`String()`函数被编译到二进制文件中, 自检对性能或二进制大小没有影响。 + + +--- +via: https://medium.com/a-journey-with-go/go-stringer-command-efficiency-through-code-generation-df49f97f3954 + +作者:[Vincent Blanchon](https://medium.com/@blanchon.vincent) +译者:[kagxin](https://github.com/kagxin) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [GCTT](https://github.com/studygolang/GCTT) 原创编译,[Go 中文网](https://studygolang.com/) 荣誉推出 \ No newline at end of file From 8f08bfa8fdae3d89e86bf235c5c458073c41fe28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E9=91=AB?= Date: Sat, 14 Nov 2020 21:41:25 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...Command-Efficiency-Through-Code-Generation.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md b/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md index 75d917c4b..12108fb46 100644 --- a/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md +++ b/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md @@ -1,36 +1,36 @@ # GO:`stringer`命令,通过代码生成提高效率 -![由Renee French创作的原始Go Gopher作品,为“ Go的旅程”创作的插图。](https://miro.medium.com/max/1400/1*lLJ2BlmnulX1WZGfUTt0vg.png) +![由Renee French创作的原始Go Gopher作品,为“ Go的旅程”创作的插图。](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/00.png) ℹ️ 这篇文章基于 Go 1.13。 `stringer`命令的目标是自动生成满足`fmt.Stringer`接口的方法。它将为指定的类型生成`String()`方法,`String()`返回的字符串用于描述该类型。 ## 例子 这个[命令的文档](https://godoc.org/golang.org/x/tools/cmd/stringer)中给我们提供了一个学习的例子,如下: -![](https://miro.medium.com/max/1400/1*vucGVFa1Qju4pXxzRjiZ9w.png) +![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/01.png) 输出如下: ``` 1 ``` 产生的日志是一个常量的值,这可能会让你感到困惑。 让我们用命令`stringer -type=Pill`生成`String()`方法吧: -![](https://miro.medium.com/max/1400/1*i-v4hKZD3vTMz-nxJaKj1A.png) +![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/02.png) 生成了新的`String()`函数,运行当前代码时输出如下: ``` Aspirin ``` 现在描述该类型的是一个字符串,而不是它实际的常量值了。 `stringer`也可以与`go generate`命令完美配合,使其功能更强大。只需在代码中添加以下指令即可: -![](https://miro.medium.com/max/1400/1*BnPEnsqwZLxt9FFR-Jxm9g.png) +![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/03.png) 然后,运行`go generate`命令将会为你所有的类型自动生成新的函数。 ## 效率 `stringer`生成了一个包含每一个字符串的`长字符串`和一个包含每一个字符串索引的数组。在我们这个例子里,读`Aspirin`即是读`长字符串`的索引7到13组成的字符串: -![](https://miro.medium.com/max/1400/1*TJs010AERv1mWUomMsu7qg.png) +![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/04.png) 但是它有多快、多高效?我们来和另外两种方案对比一下: * 硬编码`String()`函数 -![](https://miro.medium.com/max/1400/1*8HCJlcw_fP24lpooVhJT8g.png) +![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/05.png) 下面是一个包含20个常量的基准测试: ``` name time/op @@ -48,7 +48,7 @@ StringerWithSwitch-4 4.99ns ± 1% * `String()`函数输出一个map -![](https://miro.medium.com/max/1400/1*B7JjenIX_IIgWUL5Vf27XQ.png) +![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/06.png) 下面是一个包含20个常量的基准测试: ``` name time/op @@ -60,7 +60,7 @@ StringerWithMap-4 28.60ns ± 2% ## 自检器 在生成的代码中,有一些纯粹是为了校验的目的。下面是这些指令: -![](https://miro.medium.com/max/1400/1*_LaRSP60WqMzA3Lz9AhkcA.png) +![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/07.png) `stringer`将常量的名称与值一起写入每行。在本例中,`Aspirin`的值为`2`。更新常量的名称或其值会产生错误 * 更新名称但不重新生成`String()`函数: ``` From 9c345f6e513cb9d46433a8953c40fbeeed8637e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E9=91=AB?= Date: Sat, 14 Nov 2020 22:35:49 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmdl=20lint=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...5-Go-Stringer-Command-Efficiency-Through-Code-Generation.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md b/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md index 12108fb46..1ced34434 100644 --- a/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md +++ b/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md @@ -46,7 +46,7 @@ StringerWithSwitch-4 4.99ns ± 1% 常量越多,效率越高。这是有道理的。从内存中加载一个值比一些跳转指令(表示if条件的汇编指令)更具有膨胀性。 然而,当switch语句越大,跳转指令的数量就越多。从某种程度上来说,从内存中加载将会变得更有效。 -* `String()`函数输出一个map +* `String()`函数输出一个map ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/06.png) 下面是一个包含20个常量的基准测试: @@ -82,7 +82,6 @@ Pill(3) ``` 只有`String()`函数被编译到二进制文件中, 自检对性能或二进制大小没有影响。 - --- via: https://medium.com/a-journey-with-go/go-stringer-command-efficiency-through-code-generation-df49f97f3954 From 85a6a962581a1793355a2a579745ce3ec302b38b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BA=B7=E9=91=AB?= Date: Sun, 29 Nov 2020 15:13:16 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...mand-Efficiency-Through-Code-Generation.md | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md b/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md index 1ced34434..bfdfc4b10 100644 --- a/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md +++ b/translated/tech/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation.md @@ -1,92 +1,92 @@ -# GO:`stringer`命令,通过代码生成提高效率 -![由Renee French创作的原始Go Gopher作品,为“ Go的旅程”创作的插图。](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/00.png) +# GO: `stringer` 命令,通过代码生成提高效率 +![由 Renee French 创作的原始 Go Gopher 作品,为“ Go 的旅程”创作的插图。](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/00.png) ℹ️ 这篇文章基于 Go 1.13。 -`stringer`命令的目标是自动生成满足`fmt.Stringer`接口的方法。它将为指定的类型生成`String()`方法,`String()`返回的字符串用于描述该类型。 +`stringer` 命令的目标是自动生成满足 `fmt.Stringer` 接口的方法。它将为指定的类型生成 `String()` 方法, `String()` 返回的字符串用于描述该类型。 ## 例子 -这个[命令的文档](https://godoc.org/golang.org/x/tools/cmd/stringer)中给我们提供了一个学习的例子,如下: +这个[命令的文档](https://godoc.org/golang.org/x/tools/cmd/stringer) 中给我们提供了一个学习的例子,如下: ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/01.png) 输出如下: ``` 1 ``` 产生的日志是一个常量的值,这可能会让你感到困惑。 -让我们用命令`stringer -type=Pill`生成`String()`方法吧: +让我们用命令 `stringer -type=Pill` 生成 `String()` 方法吧: ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/02.png) -生成了新的`String()`函数,运行当前代码时输出如下: +生成了新的 `String()` 函数,运行当前代码时输出如下: ``` Aspirin ``` 现在描述该类型的是一个字符串,而不是它实际的常量值了。 -`stringer`也可以与`go generate`命令完美配合,使其功能更强大。只需在代码中添加以下指令即可: + `stringer` 也可以与 `go generate` 命令完美配合,使其功能更强大。只需在代码中添加以下指令即可: ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/03.png) -然后,运行`go generate`命令将会为你所有的类型自动生成新的函数。 +然后,运行 `go generate` 命令将会为你所有的类型自动生成新的函数。 ## 效率 -`stringer`生成了一个包含每一个字符串的`长字符串`和一个包含每一个字符串索引的数组。在我们这个例子里,读`Aspirin`即是读`长字符串`的索引7到13组成的字符串: +`stringer` 生成了一个包含每一个字符串的 ` 长字符串 ` 和一个包含每一个字符串索引的数组。在我们这个例子里,读 `Aspirin` 即是读 ` 长字符串 ` 的索引 7 到 13 组成的字符串: ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/04.png) 但是它有多快、多高效?我们来和另外两种方案对比一下: -* 硬编码`String()`函数 +* 硬编码 `String()` 函数 ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/05.png) -下面是一个包含20个常量的基准测试: +下面是一个包含 20 个常量的基准测试: ``` name time/op Stringer-4 4.16ns ± 2% StringerWithSwitch-4 3.81ns ± 1% ``` -包含100个常量的基准测试: +包含 100 个常量的基准测试: ``` name time/op Stringer-4 4.96ns ± 0% StringerWithSwitch-4 4.99ns ± 1% ``` -常量越多,效率越高。这是有道理的。从内存中加载一个值比一些跳转指令(表示if条件的汇编指令)更具有膨胀性。 -然而,当switch语句越大,跳转指令的数量就越多。从某种程度上来说,从内存中加载将会变得更有效。 +常量越多,效率越高。这是有道理的。从内存中加载一个值比一些跳转指令(表示 if 条件的汇编指令)更具有膨胀性。 +然而,switch 语句分支越多,跳转指令的数量就越多。从某种程度上来说,从内存中加载将会变得更有效。 -* `String()`函数输出一个map +* `String()` 函数输出一个 map ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/06.png) -下面是一个包含20个常量的基准测试: +下面是一个包含 20 个常量的基准测试: ``` name time/op Stringer-4 4.16ns ± 2% StringerWithMap-4 28.60ns ± 2% ``` -使用map要慢得多,因为它必须进行函数调用,并且在bucket中查找不像访问切片的索引那么简单。 -想了解更多关于map的信息和内部结构,我建议你阅读我的文章"[Go: Map Design by Code](https://medium.com/a-journey-with-go/go-map-design-by-code-part-ii-50d111557c08)" +使用 map 要慢得多,因为它必须进行函数调用,并且在 bucket 中查找不像访问切片的索引那么简单。 +想了解更多关于 map 的信息和内部结构,我建议你阅读我的文章 "[Go: Map Design by Code](https://medium.com/a-journey-with-go/go-map-design-by-code-part-ii-50d111557c08)" ## 自检器 在生成的代码中,有一些纯粹是为了校验的目的。下面是这些指令: ![](https://raw.githubusercontent.com/studygolang/gctt-images2/master/20200605-Go-Stringer-Command-Efficiency-Through-Code-Generation/07.png) -`stringer`将常量的名称与值一起写入每行。在本例中,`Aspirin`的值为`2`。更新常量的名称或其值会产生错误 -* 更新名称但不重新生成`String()`函数: +`stringer` 将常量的名称与值一起写入每行。在本例中,`Aspirin` 的值为 `2`。更新常量的名称或其值会产生错误 +* 更新名称但不重新生成 `String()` 函数: ``` ./pill_string.go:12:8: undefined: Aspirin ``` -* 更新值但不重新生成`String()`函数: +* 更新值但不重新生成 `String()` 函数: ``` ./pill_string.go:12:7: invalid array index Aspirin - 1 (out of bounds for 1-element array) ./pill_string.go:13:7: invalid array index Ibuprofen - 2 (index must be non-negative ``` -然而,当我们添加一个新的常量的情况下--这里下一个值为`3`,并且不更新生成的文件,`stringer`会有一个默认值: +然而,当我们添加一个新的常量的情况下 -- 这里下一个值为 `3`,并且不更新生成的文件,`stringer` 会有一个默认值: ``` Pill(3) ``` -添加这个自检不会有任何影响,因为它在编译时被删除了。可以通过查看程序生成的asm代码来确认: +添加这个自检不会有任何影响,因为它在编译时被删除了。可以通过查看程序生成的 asm 代码来确认 : ``` ➜ go tool compile -S main.go pill_string.go | grep "\"\".Pill\.[^\s]* STEXT" "".Pill.String STEXT size=275 args=0x18 locals=0x50 ``` -只有`String()`函数被编译到二进制文件中, 自检对性能或二进制大小没有影响。 +只有 `String()` 函数被编译到二进制文件中 , 自检对性能或二进制大小没有影响。 --- via: https://medium.com/a-journey-with-go/go-stringer-command-efficiency-through-code-generation-df49f97f3954 作者:[Vincent Blanchon](https://medium.com/@blanchon.vincent) 译者:[kagxin](https://github.com/kagxin) -校对:[校对者ID](https://github.com/校对者ID) +校对:[校对者 ID](https://github.com/校对者ID) 本文由 [GCTT](https://github.com/studygolang/GCTT) 原创编译,[Go 中文网](https://studygolang.com/) 荣誉推出 \ No newline at end of file