diff --git a/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md b/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md
index 00ceda7..450553b 100644
--- a/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md
+++ b/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md
@@ -1,18 +1,18 @@
-# 自动化检查Go代码中的漏洞
+# 自动化检查 Go 代码中的漏洞
- [原文链接](https://awkwardferny.medium.com/go-application-security-and-appsec-automation-made-easy-36bd2f3d520b)
- 原文作者:Fernando Diaz
- [本文永久链接](https://github.com/gocn/translator/blob/master/static/images/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md)
-- 译者:[司镜233](https://github.com/sijing233)
+- 译者:[司镜 233](https://github.com/sijing233)
- 校对:[刘思家](https://github.com/lsj1342)
-在云应用程序领域,Go是最流行的语言之一了,Kubernetes大部分的内容都是Go构建的。
+在云应用程序领域,Go 是最流行的语言之一了,Kubernetes 大部分的内容都是 Go 构建的。
-但即便如此,根据[Nautilus2022云原生威胁报告](https://info.aquasec.com/cloud-native-threat-report-2022)表明:具有恶意的个人或组织,也增加了更多目标和方式,包括CI/CD的环境、容易收到攻击的Kubernets部署和应用程序。
+但即便如此,根据[Nautilus2022 云原生威胁报告](https://info.aquasec.com/cloud-native-threat-report-2022)表明:具有恶意的个人或组织,也增加了更多目标和方式,包括 CI/CD 的环境、容易收到攻击的 Kubernets 部署和应用程序。
-随着时间的推移,针对Kubernets的攻击次数、攻击手段不断增加。根据[AquaSec](https://www.aquasec.com/)的观察显示:以Kubernets为目标的恶意攻击数量,从2020年的9%到2021年的19%,增加了10%。这也说明,保护我们Golang应用程序的安全,越来越重要。
+随着时间的推移,针对 Kubernets 的攻击次数、攻击手段不断增加。根据[AquaSec](https://www.aquasec.com/)的观察显示:以 Kubernets 为目标的恶意攻击数量,从 2020 年的 9%到 2021 年的 19%,增加了 10%。这也说明,保护我们 Golang 应用程序的安全,越来越重要。
-在这篇文章中,我将展示用扫描应用程序源代码漏洞的各种方法,以及如何将安全扫描器集成到GitLab等CI/CD平台中。我将提供一份我创建的,不安全的微服务的真实示例。
+在这篇文章中,我将展示用扫描应用程序源代码漏洞的各种方法,以及如何将安全扫描器集成到 GitLab 等 CI/CD 平台中。我将提供一份我创建的,不安全的微服务的真实示例。
@@ -20,13 +20,13 @@
## 先决条件
-- 基本了解Go编程语言
-- Git基础知识
+- 基本了解 Go 编程语言
+- Git 基础知识
- 基本了解应用程序的安全性
-- Gitlab账户(免费)
+- Gitlab 账户(免费)
- Go 1.19+
-```
+```plain
$ go versiongo version go1.19.1 darwin/amd64
```
@@ -36,11 +36,11 @@ $ go versiongo version go1.19.1 darwin/amd64
-在推送代码之前,或是将代码部署到生产级环境之前,运行安全扫描器,检测并修复漏洞。我将介绍如何用Go,使用各种不同的安全扫描器:[GoSec](](https://github.com/securego/gosec))、[GoVulnCheck](https://go.dev/blog/vuln)、[Fuzz](](https://go.dev/security/fuzz/))
+在推送代码之前,或是将代码部署到生产级环境之前,运行安全扫描器,检测并修复漏洞。我将介绍如何用 Go,使用各种不同的安全扫描器:[GoSec](](https://github.com/securego/gosec))、[GoVulnCheck](https://go.dev/blog/vuln)、[Fuzz](](https://go.dev/security/fuzz/))
-首先,我们可以开始设置一个适当的GOPATH,添加GOPATH/bin到我们的PATH,并且git clone [不安全](https://gitlab.com/awkwardferny/insecure-microservice)的微服务代码,可以在[此处](https://go.dev/doc/tutorial/compile-install)找到有关路径的详细信息。
+首先,我们可以开始设置一个适当的 GOPATH,添加 GOPATH/bin 到我们的 PATH,并且 git clone [不安全](https://gitlab.com/awkwardferny/insecure-microservice)的微服务代码,可以在[此处](https://go.dev/doc/tutorial/compile-install)找到有关路径的详细信息。
```shell
# 设置合适的 GOPATH
@@ -62,13 +62,13 @@ $ git clone git@gitlab.com:awkwardferny/insecure-microservice.git src/gitlab.com
$ cd src/gitlab.com/awkwardferny/insecure-microservice
```
-现在,我们已经正确设置了路径,并且已经clone了应用程序,我们可以开始运行我们的安全扫描器了。
+现在,我们已经正确设置了路径,并且已经 clone 了应用程序,我们可以开始运行我们的安全扫描器了。
# GoSec(源代码分析)
-我们将介绍的第一个安全扫描器是[GoSec](https://github.com/securego/gosec)。它是一种流行的Go安全扫描器,可以扫描应用程序的源代码和依赖项,检查到漏洞。它通过将您的源代码与一组规则进行模式匹配来工作。
+我们将介绍的第一个安全扫描器是[GoSec](https://github.com/securego/gosec)。它是一种流行的 Go 安全扫描器,可以扫描应用程序的源代码和依赖项,检查到漏洞。它通过将您的源代码与一组规则进行模式匹配来工作。
-如果Go模块打开(e.g.`GO111MODULE=on`) ,或者明确下载依赖项(`go get -d`),GoSec还可以自动扫描您的应用程序依赖项,来检查漏洞。现在,让我们在不安全的微服务上运行GoSec:
+如果 Go 模块打开(e.g.`GO111MODULE=on`) ,或者明确下载依赖项(`go get -d`),GoSec 还可以自动扫描您的应用程序依赖项,来检查漏洞。现在,让我们在不安全的微服务上运行 GoSec:
```shell
# 安装GoSec
@@ -93,15 +93,15 @@ G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)
这些漏洞表明我们的应用程序,有很多未捕获的异常:没有设置超时、使用了弱随机生成数。扫描返回规则出发、常见弱点枚举(CWE)、置信度、严重性和受影响的代码行。
-在典型的开发人员工作流中,发现漏洞后,开发人员可以检查CWE,获取改进提示,对受影响的代码进行代码更改,然后重新运行扫描程序,以检查解决方案。应该运行回归测试,以确保我们的应用程序逻辑仍然健全。
+在典型的开发人员工作流中,发现漏洞后,开发人员可以检查 CWE,获取改进提示,对受影响的代码进行代码更改,然后重新运行扫描程序,以检查解决方案。应该运行回归测试,以确保我们的应用程序逻辑仍然健全。
# Govulncheck(源代码分析)
-接下来是Govulncheck!Govulncheck是一个针对源代码,和应用程序依赖项的安全扫描器。Go安全团队正在积极开发它,并且在几个方面,与GoSec不同:
+接下来是 Govulncheck!Govulncheck 是一个针对源代码,和应用程序依赖项的安全扫描器。Go 安全团队正在积极开发它,并且在几个方面,与 GoSec 不同:
-首先,它由[Go漏洞数据库]((https://vuln.go.dev/))支持。
+首先,它由[Go 漏洞数据库]((https://vuln.go.dev/))支持。
其次,它只显示您的代码,实际调用的漏洞。这会减少“噪声”,并且让您知道哪些漏洞实际影响了您的应用程序。
@@ -113,7 +113,7 @@ G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)
现在,让我们试一试!
-```
+```plain
# 安装 govulncheck
$ go install golang.org/x/vuln/cmd/govulncheck@latest
@@ -139,7 +139,7 @@ More info: https://pkg.go.dev/vuln/GO-2020-0016
-您可以看到扫描器,向我们提供了漏洞规则参考、说明、受影响的代码行、漏洞依赖项、解决方案以及附加信息的链接。因为我在我的应用程序中使用***github.com/ulikunitz/xz@v0.5.7作为*依赖*项并调用***xz.Reader.Read,所以我的应用程序容易受到[DDoS](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/)攻击。这个漏洞是由Go 漏洞数据库中的[GO-2020-016规则检测到的。](https://github.com/golang/vulndb/blob/master/data/reports/GO-2020-0016.yaml)
+您可以看到扫描器,向我们提供了漏洞规则参考、说明、受影响的代码行、漏洞依赖项、解决方案以及附加信息的链接。因为我在我的应用程序中使用***github.com/ulikunitz/xz@v0.5.7 作为*依赖*项并调用***xz.Reader.Read,所以我的应用程序容易受到[DDoS](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/)攻击。这个漏洞是由 Go 漏洞数据库中的[GO-2020-016 规则检测到的。](https://github.com/golang/vulndb/blob/master/data/reports/GO-2020-0016.yaml)
在典型的工作流程中,开发人员会更新依赖版本,然后重新运行扫描器以及*单元*和*功能*测试,以确保应用程序不会中断。
@@ -179,13 +179,13 @@ func add(a string, b string) (c int, e error) {
-我们可以看到 `FuzzAdd() `的编写类似于单元测试。我们通过添加`f.Fuzz(func(t \*testing.T, a string, b string)`来启用模糊测试,它调用`add( a string, b string )`函数,为变量`a`和`b`提供随机数据。然后,将它和预期值结果,进行比较。
+我们可以看到 `FuzzAdd()`的编写类似于单元测试。我们通过添加`f.Fuzz(func(t \*testing.T, a string, b string)`来启用模糊测试,它调用`add( a string, b string )`函数,为变量`a`和`b`提供随机数据。然后,将它和预期值结果,进行比较。
-`add()`函数,简单地将2 个字符串转换为整数,然后将它们相加并返回结果。
+`add()`函数,简单地将 2 个字符串转换为整数,然后将它们相加并返回结果。
`FuzzAdd ()`测试可以使用[种子数据](https://go.dev/security/fuzz/#glos-seed-corpus)`f.Add("1", “2”),`正确运行,但是当存在格式错误或随机数据时会发生什么情况?让我们运行模糊测试并找出:
-```
+```plain
# 运行 fuzz 测试
$ go test ./internal/logic -fuzz FuzzAdd
```
@@ -203,11 +203,11 @@ $ go test ./internal/logic -fuzz FuzzAdd
FAIL
```
-导致这个错误,是因为传递了字母A,而不是可以转换为整数的字符串。Fuzz还在testdata目录下,生成了一个种子语料库,可以用来再次测试这个特定的故障。
+导致这个错误,是因为传递了字母 A,而不是可以转换为整数的字符串。Fuzz 还在 testdata 目录下,生成了一个种子语料库,可以用来再次测试这个特定的故障。
-解决这个问题的一个方式,是在add()函数中,简单地返回err,而不是nil。并期望在FuzzAdd()中,返回非整数可转换字符串的错误。
+解决这个问题的一个方式,是在 add()函数中,简单地返回 err,而不是 nil。并期望在 FuzzAdd()中,返回非整数可转换字符串的错误。
-我们还可以考虑,仅将整数值设置为0,并记录错误。如下所示,这仅仅取决于,我们要实现的目标。
+我们还可以考虑,仅将整数值设置为 0,并记录错误。如下所示,这仅仅取决于,我们要实现的目标。
```go
func add(a string, b string) (c int, e error) {
@@ -225,25 +225,25 @@ func add(a string, b string) (c int, e error) {
}
```
-有关模糊测试的更多高级用法,请查看 [Go模糊测试教程](https://go.dev/doc/tutorial/fuzz).
+有关模糊测试的更多高级用法,请查看 [Go 模糊测试教程](https://go.dev/doc/tutorial/fuzz).
-# 使用GitLab实现自动化扫描
+# 使用 GitLab 实现自动化扫描
-如果可以自动运行安全扫描器来搜索Go应用程序中的漏洞,这样我们就可以在每次推送代码时,在功能分支上运行扫描器。
+如果可以自动运行安全扫描器来搜索 Go 应用程序中的漏洞,这样我们就可以在每次推送代码时,在功能分支上运行扫描器。
这会在代码投入生产之前,解决安全问题,并且不必在每次更改代码时,都手动运行扫描程序,从而节省了我们的时间。
-这些扫描器,可以通过在GitLab中,创建CI/CD管道来实现自动化。管道可以在每次将代码推送到分支时,自动运行这些扫描。我们将查看[GitLab CI yaml](https://gitlab.com/awkwardferny/insecure-microservice/-/blob/master/.gitlab-ci.yml),它在下面生成了一个CI/CD管道。
+这些扫描器,可以通过在 GitLab 中,创建 CI/CD 管道来实现自动化。管道可以在每次将代码推送到分支时,自动运行这些扫描。我们将查看[GitLab CI yaml](https://gitlab.com/awkwardferny/insecure-microservice/-/blob/master/.gitlab-ci.yml),它在下面生成了一个 CI/CD 管道。
首先,我们看到的是,将按照提供的顺序,在管道中运行的阶段:
-```
+```plain
stages:
- build
- test
@@ -253,7 +253,7 @@ The **build** stage makes sure the application even builds before proceeding. If
构建阶段,确保是在构建应用程序之前。如果您已经容器化了您的应用程序,那么在这个阶段,您最好也测试一下,是否可以构建容器镜像:
-```
+```plain
build:
image: golang:alpine
stage: build
@@ -268,7 +268,7 @@ build:
-```
+```plain
unit:
image: golang:alpine
stage: test
@@ -322,7 +322,7 @@ fuzz:
-这就是将单元测试、模糊测试和安全扫描器,集成到CI/CD管道中的方法。这让生活变的更轻松,并且无需每次都手动运行所有内容。
+这就是将单元测试、模糊测试和安全扫描器,集成到 CI/CD 管道中的方法。这让生活变的更轻松,并且无需每次都手动运行所有内容。
# 代码审查和安全编码实践
@@ -362,5 +362,5 @@ fuzz:
仪表板类型的视图将是理想的,这样您就可以有效地分类和管理漏洞,引导您找到应该首先解决的问题。
-好了,自动化检查Go代码中的漏洞!感谢阅读,希望您喜欢这篇文章。
+好了,自动化检查 Go 代码中的漏洞!感谢阅读,希望您喜欢这篇文章。
diff --git a/2023/w04_Implementing_clean_architecture_in_Go.md b/2023/w04_Implementing_clean_architecture_in_Go.md
index d9e4126..c550bda 100644
--- a/2023/w04_Implementing_clean_architecture_in_Go.md
+++ b/2023/w04_Implementing_clean_architecture_in_Go.md
@@ -6,13 +6,13 @@
- 译者:[zxmfke](https://github.com/zxmfke)
- 校对:
-已经有很多关于 [简洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)的文章了。它的主要价值在于能够维护无副作用的领域层,使我们能够不需要利用沉重的mock来测试核心业务逻辑。
+已经有很多关于 [简洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)的文章了。它的主要价值在于能够维护无副作用的领域层,使我们能够不需要利用沉重的 mock 来测试核心业务逻辑。
通过写一个无需依赖的核心领域逻辑,以及外部适配器(成为它的数据库存储或者 API 层)来实现的。这些适配器依赖于领域,而不是领域依赖适配器。
在这篇文章,我们会看一下简洁架构是如何实现一个简单的 Go 项目。我们会提及一些额外的主题,例如容器化以及用 Swagger 实现 OpenAPI 规范。
-虽然我将在文章中高亮了感兴趣的点,但你可以在 [我的Github](https://github.com/Wkalmar/toggl-deck-management-api) 上看看整个项目。
+虽然我将在文章中高亮了感兴趣的点,但你可以在 [我的 Github](https://github.com/Wkalmar/toggl-deck-management-api) 上看看整个项目。
## 项目需求
@@ -268,7 +268,7 @@ func main() {
-一些读者可能对根据上述要求,创建牌组这个路由将参数作为URL请求的一部分感到困惑,可能会考虑让这个路由用 GET 请求而不是 POST。 然而,GET 请求的一个重要前提是,它们表现出[一致性](https://www.restapitutorial.com/lessons/idempotency.html),即每次请求的结果是一致的,而这个路由不是这样的。这就是我们坚持使用 POST 的原因。
+一些读者可能对根据上述要求,创建牌组这个路由将参数作为 URL 请求的一部分感到困惑,可能会考虑让这个路由用 GET 请求而不是 POST。 然而,GET 请求的一个重要前提是,它们表现出[一致性](https://www.restapitutorial.com/lessons/idempotency.html),即每次请求的结果是一致的,而这个路由不是这样的。这就是我们坚持使用 POST 的原因。
路由对应的 Handler 遵循相同的模式。我们解析查询参数,根据这些参数创建一个领域实体,对其进行操作,更新存储并返回专属的 DTO。让我们来看看更多的细节。
@@ -455,7 +455,7 @@ r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
这些都完成之后,那我们现在可以运行我们的应用程序,看看通过 Swagger 生成的文档。
-## API容器化
+## API 容器化
最后但并非最不重要的是我们将如何部署我们的应用程序。传统的方法是在一个专门的服务器上安装,并在安装的服务器上运行应用程序。
@@ -502,4 +502,4 @@ docker run -it --rm -p 8080:8080
## 总结
-在这篇文章中,我们已经介绍了在 Go 中编写简洁架构 API 的整体过程。从经过测试的领域开始,为其提供一个API 层,使用 OpenAPI 标准对其进行记录,并将我们的 runtime 与应用程序打包在一起,从而简化了部署过程。
\ No newline at end of file
+在这篇文章中,我们已经介绍了在 Go 中编写简洁架构 API 的整体过程。从经过测试的领域开始,为其提供一个 API 层,使用 OpenAPI 标准对其进行记录,并将我们的 runtime 与应用程序打包在一起,从而简化了部署过程。
\ No newline at end of file
diff --git a/2023/w05_Introducing Ristretto A High-Performance Go Cache - Dgraph Blog.md b/2023/w05_Introducing Ristretto A High-Performance Go Cache - Dgraph Blog.md
index 4d81d2c..c6da1fc 100644
--- a/2023/w05_Introducing Ristretto A High-Performance Go Cache - Dgraph Blog.md
+++ b/2023/w05_Introducing Ristretto A High-Performance Go Cache - Dgraph Blog.md
@@ -1,12 +1,12 @@
-# Ristretto 简介: 一个高性能GO缓存
+# Ristretto 简介: 一个高性能 GO 缓存
- 原文地址:[Introducing Ristretto: A High-Performance Go Cache](https://dgraph.io/blog/post/introducing-ristretto-high-perf-go-cache/)
- 原文作者:[Dmitry Filimonov](https://github.com/petethepig)
-- 本文永久链接:[Ristretto 简介: 一个高性能GO缓存](https://github.com/gocn/translator/blob/master/2023/w05_Introducing%20Ristretto%20A%20High-Performance%20Go%20Cache%20-%20Dgraph%20Blog.md)
+- 本文永久链接:[Ristretto 简介: 一个高性能 GO 缓存](https://github.com/gocn/translator/blob/master/2023/w05_Introducing%20Ristretto%20A%20High-Performance%20Go%20Cache%20-%20Dgraph%20Blog.md)
- 译者:[784909593](https://github.com/784909593)
- 校对:[b8kings0ga](https://github.com/b8kings0ga)
-**这个博客登上了 Golang [subreddit](https://www.reddit.com/r/golang/comments/d6taoq/introducing_ristretto_a_highperformance_go_cache/) 的顶部,并且在 [Hacker News](https://news.ycombinator.com/item?id=21023949) 的trending上排在前十位。 一定要在那里参与讨论,并通过给我们一个 [star](https://github.com/dgraph-io/dgraph),表达对我们的喜欢。**
+**这个博客登上了 Golang [subreddit](https://www.reddit.com/r/golang/comments/d6taoq/introducing_ristretto_a_highperformance_go_cache/) 的顶部,并且在 [Hacker News](https://news.ycombinator.com/item?id=21023949) 的 trending 上排在前十位。 一定要在那里参与讨论,并通过给我们一个 [star](https://github.com/dgraph-io/dgraph),表达对我们的喜欢。**
经过六个月的研发,我们自豪的宣布**缓存 [Ristretto](https://github.com/dgraph-io/ristretto):一个高性能、并发、可设置内存上限的 Go 缓存**的初始版本。他是抗争用、扩展性好、提供稳定的高命中率。
@@ -21,7 +21,7 @@
2. 缓存命中率高;
3. Memory-bounded (限制为可配置的最大内存使用量);
4. 随着核数和协程数量的增加而扩展;
-5. 在非随机key访问(例如 Zipf)分布下很好的扩展;
+5. 在非随机 key 访问(例如 Zipf)分布下很好的扩展;
发布了[博客文章](https://blog.dgraph.io/post/caching-in-go/)之后,我们组建了一个团队来解决其中提到的挑战,并创建了一个值得与非 Go 语言缓存实现进行比较的 Go 缓存库。特别的,[Caffeine](https://github.com/ben-manes/caffeine) 这是一个基于 Java 8 的高性能、近乎最优的缓存库。许多基于 Java 8 的数据库都在使用它, 比如 Cassandra,HBase,和 Neo4j。[这里](http://highscalability.com/blog/2016/1/25/design-of-a-modern-cache.html)有一篇关于 Caffeine 设计的文章。
@@ -31,7 +31,7 @@
在我们开始讲解 [Ristretto](https://github.com/dgraph-io/ristretto) 的设计之前, 这有一个代码片段展示了如何使用它:
-```
+```plain
func main() {
cache, err := ristretto.NewCache(&ristretto.Config{
NumCounters: 1e7, // key 跟踪频率为(10M)
@@ -71,13 +71,13 @@ cache.Del("key")
缓存的核心是一个 hash map 和关于进入和出去的规则。如果 hash map 表现不佳,那么整个缓存将受到影响。 与 Java 不同, Go 没有无锁的并发 hashmap。相反,Go 的线程安全是必须通过显式获取互斥锁来达到。
-我们尝试了多种实现方式(使用 Ristretto 中的`store`接口),发现`sync.Map`在读取密集型工作负载方面表现良好,但在写入工作负载方面表现不佳。考虑没有 thread-local 存储,**我们发现使用分片的互斥锁包装的 Go map具有最佳的整体性能。** 特别是,我们选择使用 256 个分片,以确保即使在 64 核服务器上也能表现良好。
+我们尝试了多种实现方式(使用 Ristretto 中的`store`接口),发现`sync.Map`在读取密集型工作负载方面表现良好,但在写入工作负载方面表现不佳。考虑没有 thread-local 存储,**我们发现使用分片的互斥锁包装的 Go map 具有最佳的整体性能。** 特别是,我们选择使用 256 个分片,以确保即使在 64 核服务器上也能表现良好。
使用基于分片的方法,我们还需要找到一种快速方法来计算 key 应该进入哪个分片。这个要求和对 key 太长消耗太多内存的担忧,导致我们对 key 使用 `uint64`,而不是存储整个 key。理由是我们需要在多个地方使用 key 的哈希值,并且在入口处执行一次允许我们重用该哈希值,避免任何更多的计算。
-**为了生成快速 hash,我们从 Go Runtime 借用了 [runtime.memhash](https://github.com/dgraph-io/ristretto/blob/master/z/rtutil.go#L42-L44)** 该函数使用汇编代码快速生成哈希。 请注意,这个 hash 有一个随机化器,每当进程启动时都会初始化,这意味着相同的key不会在下一次进程运行时生成相同的哈希。 但是,这对于非持久缓存来说没问题。 在我们的[实验](https://github.com/dgraph-io/ristretto/blob/master/z/rtutil_test.go#L11-L44), 我们发现它可以在 10ns 内散列 64 字节的 key。
+**为了生成快速 hash,我们从 Go Runtime 借用了 [runtime.memhash](https://github.com/dgraph-io/ristretto/blob/master/z/rtutil.go#L42-L44)** 该函数使用汇编代码快速生成哈希。 请注意,这个 hash 有一个随机化器,每当进程启动时都会初始化,这意味着相同的 key 不会在下一次进程运行时生成相同的哈希。 但是,这对于非持久缓存来说没问题。 在我们的[实验](https://github.com/dgraph-io/ristretto/blob/master/z/rtutil_test.go#L11-L44), 我们发现它可以在 10ns 内散列 64 字节的 key。
-```
+```plain
BenchmarkMemHash-32 200000000 8.88 ns/op
BenchmarkFarm-32 100000000 17.9 ns/op
BenchmarkSip-32 30000000 41.1 ns/op
@@ -103,7 +103,7 @@ BenchmarkFnv-32 20000000 70.6 ns/op
存储在 pool 中的任何 item 都可能随时自动删除,[不另行](https://golang.org/pkg/sync/#Pool)通知。_这引入了一个级别的有损行为。_ Pool 中的每个 item 实际上都是一批 key。当批次填满时,它会被推送到一个 chan。chan 大小故意保持较小,以避免消耗太多 CPU 周期来处理它。 如果 chan 已满,则丢弃该批次。_这引入了二级有损行为。_ 一个 goroutine 从内部 chan 中获取这批数据并处理这些 key,更新它们的命中计数器。
-```
+```plain
AddToLossyBuffer(key):
stripe := b.pool.Get().(*ringStripe)
stripe.Push(key)
@@ -135,7 +135,7 @@ Set buffer 的要求与 Get 略有不同。在 Gets 中,我们缓冲 key,只
与 Gets 一样,此方法旨在优化抗争用性。 但是,有一些注意事项,如下所述。
-```
+```plain
select {
case c.setBuf <- &item{key: hash, val: val, cost: cost}:
return true
@@ -170,7 +170,7 @@ default:
_无限大的缓存实际上是不可能的。_ 缓存的大小必须有界。许多缓存库会将缓存大小视为元素的数量。我们发现这种方法 _很幼稚_。当然,它适用于值大小相同的工作负载。然而,大多数工作负载具有可变大小的值。一个值可能需要几个字节,另一个可能需要几千字节,还有一个需要几兆字节。将它们视为具有相同的内存成本是不现实的。
-**在 [Ristretto](https://github.com/dgraph-io/ristretto) 中, 我们将成本附加到每个 key-value** 用户可以在调用 Set 时指定该成本是多少。我们将此成本计入缓存的 MaxCost。 当缓存满负荷运行时,一个 _重_ 的 item 可能会取代许多 _轻_ 的item。 这种机制很好,因为它适用于所有不同的工作负载,包括每个 key-value 成本为 1 的朴素方法。
+**在 [Ristretto](https://github.com/dgraph-io/ristretto) 中, 我们将成本附加到每个 key-value** 用户可以在调用 Set 时指定该成本是多少。我们将此成本计入缓存的 MaxCost。 当缓存满负荷运行时,一个 _重_ 的 item 可能会取代许多 _轻_ 的 item。 这种机制很好,因为它适用于所有不同的工作负载,包括每个 key-value 成本为 1 的朴素方法。
#### 通过 TinyLFU 的准入策略
@@ -194,13 +194,13 @@ TinyLFU 还通过`重置`功能维护 key 访问的新近度。每 N 个 key 递
#### 通过采样 LFU 的驱逐策略
-当缓存达到容量时,每个传入的 key 都应替换缓存中存在的一个或多个 key。 不仅如此,**传入 key 的 ε 应该高于被驱逐 key 的 ε。**为了找到一个 ε 低的 key,我们使用 Go map 迭代提供的自然[随机性](https://blog.golang.org/go-maps-in-action)来选择key样本并循环,在它们之上找到具有最低ε的key。
+当缓存达到容量时,每个传入的 key 都应替换缓存中存在的一个或多个 key。 不仅如此,**传入 key 的 ε 应该高于被驱逐 key 的 ε。**为了找到一个 ε 低的 key,我们使用 Go map 迭代提供的自然[随机性](https://blog.golang.org/go-maps-in-action)来选择 key 样本并循环,在它们之上找到具有最低ε的 key。
然后我们将这个 key 的 ɛ 与传入的 key 进行比较。如果传入的 key 具有更高的 ε,则该 key 将被逐出(_驱逐策略_)。否则,传入的 key 将被拒绝(_准入策略_)。重复此机制,直到传入 key 的成本可以放入缓存中。因此,一个传入的 key 可能取代一个以上的 key。_注意,传入 key 的成本在选择驱逐 key 时不起作用。_
**使用这种方法,在各种工作负载下,命中率与精确 LFU 策略的误差在 1% 以内。** 这意味着我们在同一个包中获得了准入策略、保守内存使用和较低争用的好处。
-```
+```plain
// 准入和驱逐算法的片段
incHits := p.admit.Estimate(key)
for ; room < 0; room = p.evict.roomLeft(cost) {
@@ -238,7 +238,7 @@ for ; room < 0; room = p.evict.roomLeft(cost) {
**为了实现可扩展性,我们确保每个原子计数器完全占据一个完整的 cache line。**所以,每个核在不同的 cache line 上工作。Ristretto 通过为每个 metric 分配 256 个 uint64 来使用它,在每个活动的 uint64 之间留下 9 个未使用的 uint 64。为了避免额外的计算,重用 key hash 值去决定要增加哪个 uint64。
-```
+```plain
Add:
valp := p.all[t]
// 通过在两个将递增的原子计数器之间填充至少 64 字节的空间来避免 false sharing。
@@ -274,7 +274,7 @@ return total
##### 数据库
-这个 trace 被描述为“在一个商业数据库上,一个商业网站正运行一个ERP应用,一个数据库服务运行在上面“
+这个 trace 被描述为“在一个商业数据库上,一个商业网站正运行一个 ERP 应用,一个数据库服务运行在上面“
![命中率: 商业数据库](https://dgraph.io/blog/images/rt-hit-db.svg?sanitize=true)
@@ -322,5 +322,5 @@ return total
## 结论
-我们的目标是让缓存库与 Caffeine 竞争。虽然没有完全实现, 但我们确实通过使用其他人可以学习的一些新技术,创造了比目前Go世界中大多数其他人[**更好**](https://en.wikipedia.org/wiki/Ristretto)的东西。
+我们的目标是让缓存库与 Caffeine 竞争。虽然没有完全实现, 但我们确实通过使用其他人可以学习的一些新技术,创造了比目前 Go 世界中大多数其他人[**更好**](https://en.wikipedia.org/wiki/Ristretto)的东西。
在 Dgraph 中使用此缓存的一些初步实验看起来很有希望。并且我们希望将 [Ristretto](https://github.com/dgraph-io/ristretto) 整合到 [Dgraph](https://github.com/dgraph-io/dgraph) 和 [Badger](https://github.com/dgraph-io/badger) 在接下来的几个月里. 一定要[查看它](https://github.com/dgraph-io/ristretto),也许可以使用 Ristretto 来加快您的工作负载。
diff --git a/2023/w06_Go's_garbage_collector.md b/2023/w06_Go's_garbage_collector.md
index 1e6fc6d..07740dc 100644
--- a/2023/w06_Go's_garbage_collector.md
+++ b/2023/w06_Go's_garbage_collector.md
@@ -8,17 +8,17 @@
> Go \[>= v1.5\]的新垃圾回收器是一种并发的三色标记清除回收器,这个想法最早是由 [Dijkstra 在 1978](https://github.com/rubinius/rubinius-website-archive/blob/cf54187d421275eec7d2db0abd5d4c059755b577/_posts/2013-06-22-concurrent-garbage-collection.markdown) 年提出的。
-Go 团队一直在密切关注并改进 Go 语言的垃圾回收器。从每50毫秒一次的10毫秒 STW 暂停到每次GC有两个 500μs 的 STW 暂停,整个改进过程可以在[这里](https://blog.golang.org/ismmkeynote)找到。
+Go 团队一直在密切关注并改进 Go 语言的垃圾回收器。从每 50 毫秒一次的 10 毫秒 STW 暂停到每次 GC 有两个 500μs 的 STW 暂停,整个改进过程可以在[这里](https://blog.golang.org/ismmkeynote)找到。
-长期从事 Go 开发,我一直对其性能感到畏惧,因此我决定深入了解其中的机制,比如 Go 语言为什么如此高效和充满前途,了解它使用的是什么样的垃圾回收器,goroutine 如何在 OS 线程上多路复用,如何对 Go 程序进行性能分析,Go 运行时是如何工作的等等。在这篇文章中,我们将着重探讨Go的垃圾回收器是如何工作的。
+长期从事 Go 开发,我一直对其性能感到畏惧,因此我决定深入了解其中的机制,比如 Go 语言为什么如此高效和充满前途,了解它使用的是什么样的垃圾回收器,goroutine 如何在 OS 线程上多路复用,如何对 Go 程序进行性能分析,Go 运行时是如何工作的等等。在这篇文章中,我们将着重探讨 Go 的垃圾回收器是如何工作的。
-在浏览互联网时,我发现了很多关于Go语言垃圾回收器的赞誉,而我对垃圾回收器的概念和工作原理只有一个抽象的理解,于是我开始阅读和学习,并在[这里](https://agrim123.github.io/posts/garbage-collection.html)记录了一些关于垃圾回收的笔记。
+在浏览互联网时,我发现了很多关于 Go 语言垃圾回收器的赞誉,而我对垃圾回收器的概念和工作原理只有一个抽象的理解,于是我开始阅读和学习,并在[这里](https://agrim123.github.io/posts/garbage-collection.html)记录了一些关于垃圾回收的笔记。
这篇博客仅仅是我在阅读一些关于 Go 的垃圾回收器及其演变历程的博客后整理出的一些想法和结论的随笔。
所以,让我们开始吧。
-```
+```plain
紧紧抓住,伙计,这将是一场精彩的旅程。
```
@@ -38,7 +38,7 @@ Go 是一种内存管理语言,这意味着大多数时候你不必担心手
简单的 STW 标记/清除的问题在随着你增加核心和扩大你的堆或分配率时会变得非常糟糕。
-## Go的并发收集器
+## Go 的并发收集器
Go 现在的 GC 不是 **“分代”回收** (一种垃圾回收算法)的。它只在后台运行一个普通的标记/清除。这有一些缺点:
@@ -76,7 +76,7 @@ func stubbornGoroutine(numbers []int32) int {
这种情况可能会导致垃圾回收无法开始。因为当收集器等待时,其他处理器不能服务任何其他协程。因此,协程必须在合理的时间内进行函数调用。
-> 如果一个goroutine没有调用函数,它不会被抢占,并且在任务结束之前它的 P 不会释放。这将迫使 “Stop the World” 等待它。
+> 如果一个 goroutine 没有调用函数,它不会被抢占,并且在任务结束之前它的 P 不会释放。这将迫使 “Stop the World” 等待它。
### 标记阶段 (并发)
@@ -90,7 +90,7 @@ func stubbornGoroutine(numbers []int32) int {
如果收集器确定它需要减缓分配,它将会招募应用程序的 Goroutine 协助 Marking 工作,这称为 Mark Assist。任何应用程序 Goroutine 在 Mark Assist 中的时间量与它对堆内存的数据添加量成比例。
-> Mark Assist 可以帮助更快地完成收集。
+> Mark Assist 可以帮助更快地完成收集。
收集器的一个目标是消除对 Mark Assist 的需求。如果任意一次收集最终需要大量的 Mark Assist,收集器可以更早开始下一次垃圾收集,以减少下一次收集所需的 Mark Assist 数量。
@@ -98,7 +98,7 @@ func stubbornGoroutine(numbers []int32) int {
一旦标记工作完成,下一阶段是标记终止。这个阶段将关闭写屏障,执行各种清理任务以及计算下一个回收目标的时刻。在标记阶段处于紧密循环的协程也可能导致标记终止 STW 延迟延长。
-回收完成后,应用程序协程可以再次使用每个P,应用程序将回到全速。
+回收完成后,应用程序协程可以再次使用每个 P,应用程序将回到全速。
### 并发清除
@@ -108,13 +108,13 @@ func stubbornGoroutine(numbers []int32) int {
## 如何让运行时知道什么时候开始回收垃圾?
-收集器有一个步伐算法,用于确定何时开始收集。节奏的建模类似于一个控制问题,它试图找到启动 GC 周期的正确时间,以达到目标堆大小目标。Go 的默认步伐控制器将尝试在堆大小加倍时触发 GC 周期。它通过在当前 GC 周期的标记终止阶段设置下一个堆触发大小来实现这一点。因此,在标记所有活动内存后,它可以决定在当前活动集的总堆大小是目前活动集的2倍时触发下一个 GC。2倍的值来自运行时使用的变量`GOGC`,用于设置触发比率。
+收集器有一个步伐算法,用于确定何时开始收集。节奏的建模类似于一个控制问题,它试图找到启动 GC 周期的正确时间,以达到目标堆大小目标。Go 的默认步伐控制器将尝试在堆大小加倍时触发 GC 周期。它通过在当前 GC 周期的标记终止阶段设置下一个堆触发大小来实现这一点。因此,在标记所有活动内存后,它可以决定在当前活动集的总堆大小是目前活动集的 2 倍时触发下一个 GC。2 倍的值来自运行时使用的变量`GOGC`,用于设置触发比率。
一种错误的观念是认为减缓收集器的速度是提高性能的方法。这个想法是,如果你可以延迟下一次收集的开始,那么你就是在延迟它造成的延迟。对收集器的同情并不是减缓节奏。
___
-Go 1.5 在2015年8月发布,带有新的低暂停并发垃圾收集器,包括实现了[节奏算法](https://docs.google.com/document/d/1wmjrocXIWTr1JxU-3EQBI6BK6KgtiFArkG47XK73xIQ/edit#heading=h.4801yvqy4taz)。
+Go 1.5 在 2015 年 8 月发布,带有新的低暂停并发垃圾收集器,包括实现了[节奏算法](https://docs.google.com/document/d/1wmjrocXIWTr1JxU-3EQBI6BK6KgtiFArkG47XK73xIQ/edit#heading=h.4801yvqy4taz)。
___
@@ -138,7 +138,7 @@ ___
- 找到最佳的一致节奏。
- 最小化每次收集的持续时间、STW 和 Mark Assist。
-## 有两个控制垃圾回收的开关。
+## 有两个控制垃圾回收的开关
正如 Rick Hudson 在[该文](https://blog.golang.org/ismmkeynote)中谈到。
diff --git a/2023/w07_Go_1_20_Experiment_Memory_Arenas_vs_Traditional_Memory_Management.md b/2023/w07_Go_1_20_Experiment_Memory_Arenas_vs_Traditional_Memory_Management.md
index 3a666dc..92fd99d 100644
--- a/2023/w07_Go_1_20_Experiment_Memory_Arenas_vs_Traditional_Memory_Management.md
+++ b/2023/w07_Go_1_20_Experiment_Memory_Arenas_vs_Traditional_Memory_Management.md
@@ -16,16 +16,16 @@
## 简介
-Go 1.20 引入了一个实验性的内存管理概念 "arenas",可以用来提高Go程序的性能。在本博客文章中,我们将探讨:
+Go 1.20 引入了一个实验性的内存管理概念 "arenas",可以用来提高 Go 程序的性能。在本博客文章中,我们将探讨:
- 什么是 arenas
- 它们是如何工作的
- 如何确定你的程序是否可以从使用 arenas 中受益
-- 我们如何使用arenas优化我们的一项服务
+- 我们如何使用 arenas 优化我们的一项服务
## 什么是内存 Arenas
-Go语言是一种利用垃圾回收机制的编程语言,这意味着运行时会自动帮助程序员管理内存的分配和释放。这消除了手动内存管理的需求,但也带来了代价:
+Go 语言是一种利用垃圾回收机制的编程语言,这意味着运行时会自动帮助程序员管理内存的分配和释放。这消除了手动内存管理的需求,但也带来了代价:
**Go 运行时必须跟踪 \*每个\* 分配的对象,导致性能开销增加。**
@@ -45,17 +45,17 @@ Arenas 提供了一种解决这个问题的方法,通过减少与许多小分
![2](https://github.com/gocn/translator/blob/master/static/images/2023/w07-Go-1-20-Experiment-Memory-Arenas-vs-Traditional-Memory-Management/2.png)
-你可以看到内存分配(`533.30 M`)的大部分来自代码的一个区域 - 这是在底部调用函数`InsertStackA`的紫色节点。鉴于它代表65%的分配,这是使用 arenas 的好候选者。但是,通过减少这些分配是否可以获得足够的性能收益?让我们看看同一服务的CPU分析(`cpu`):
+你可以看到内存分配(`533.30 M`)的大部分来自代码的一个区域 - 这是在底部调用函数`InsertStackA`的紫色节点。鉴于它代表 65%的分配,这是使用 arenas 的好候选者。但是,通过减少这些分配是否可以获得足够的性能收益?让我们看看同一服务的 CPU 分析(`cpu`):
![3](https://github.com/gocn/translator/blob/master/static/images/2023/w07-Go-1-20-Experiment-Memory-Arenas-vs-Traditional-Memory-Management/3.png)
几件事情很突出:
-- 程序在相同的`InsertStackA`函数中花费了很多CPU时间,因此显然有潜在的重要性能改进潜力。
-- 如果搜索`runtime.mallocgc`(底部的多个粉色节点),你会发现该函数在各种不同的地方频繁调用,它占用了我们总执行时间的约14%。
-- 大约5%的CPU时间花费在`runtime.gcBgMarkWorker`(位于火焰图右侧的粉色节点)上。
+- 程序在相同的`InsertStackA`函数中花费了很多 CPU 时间,因此显然有潜在的重要性能改进潜力。
+- 如果搜索`runtime.mallocgc`(底部的多个粉色节点),你会发现该函数在各种不同的地方频繁调用,它占用了我们总执行时间的约 14%。
+- 大约 5%的 CPU 时间花费在`runtime.gcBgMarkWorker`(位于火焰图右侧的粉色节点)上。
-因此,理论上,如果我们优化了这个程序中的所有分配,我们可以减少14%+5%= 19%的CPU时间。这将转化为我们所有客户的19%成本节约和延迟改进。在实践中,不太可能真正使这些数字降到零,但这仍然是应用程序执行的重要工作,可能值得优化。
+因此,理论上,如果我们优化了这个程序中的所有分配,我们可以减少 14%+5%= 19%的 CPU 时间。这将转化为我们所有客户的 19%成本节约和延迟改进。在实践中,不太可能真正使这些数字降到零,但这仍然是应用程序执行的重要工作,可能值得优化。
## 我们做出的优化
@@ -63,14 +63,14 @@ Arenas 提供了一种解决这个问题的方法,通过减少与许多小分
- 首先,我们创建了[一个包装组件](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-70ab4bbe796a97ad1a47d7970504296eff36b5307527ae2806d2b50f94f83a45),负责处理切片或结构的分配。如果启用了 arenas ,此组件使用 arenas 分配切片,否则使用标准“make”函数。我们通过使用构建标记(`//go:build goexperiment.arenas`)实现此目的。这允许在构建时轻松地在 arenas 分配和标准分配之间切换
- 然后,我们在解析器代码周围添加了[初始化](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-32bf8c53a15c8a5f7eb424b21c8502dc4905ec3caa28fac50f64277361ae746fR417)和[清理](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-34edf37e55842273380ee6cb31c9245f31ed25aa6d7898b0f2c25145f17d8ea0R170)调用 arenas
-- 接下来,我们[用我们的包装组件中的make调用替换了常规的`make`调用](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-abe15b6d3634170650f86bb7283aa15265de2197cffa969deda2dd5b26fcecd9R89-R92)
+- 接下来,我们[用我们的包装组件中的 make 调用替换了常规的`make`调用](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-abe15b6d3634170650f86bb7283aa15265de2197cffa969deda2dd5b26fcecd9R89-R92)
- 最后,我们在启用了 arenas 的情况下构建了 pyroscope,并逐渐部署到了我们的 [Pyroscope Cloud](https://pyroscope.io/pricing) 生产环境中。
## 我们 Arenas 实验的结论
![4](https://github.com/gocn/translator/blob/master/static/images/2023/w07-Go-1-20-Experiment-Memory-Arenas-vs-Traditional-Memory-Management/4.png)
-上面的火焰图表示我们实施更改后的配置文件。您可以看到,许多`runtime.mallocgc`调用现在已经消失,但被 arenas 特定的等效项(`runtime.(*userArena).alloc`)替代,您也可以看到垃圾回收开销减少了一半。仅从火焰图上看准确的节省量很难看出,但是当我们查看结合了火焰图和AWS指标的CPU使用情况的 Grafana 仪表板时,我们发现CPU使用率大约减少了8%。这直接转化为该特定服务的云账单上的8%费用节省,使其成为一项有价值的改进。
+上面的火焰图表示我们实施更改后的配置文件。您可以看到,许多`runtime.mallocgc`调用现在已经消失,但被 arenas 特定的等效项(`runtime.(*userArena).alloc`)替代,您也可以看到垃圾回收开销减少了一半。仅从火焰图上看准确的节省量很难看出,但是当我们查看结合了火焰图和 AWS 指标的 CPU 使用情况的 Grafana 仪表板时,我们发现 CPU 使用率大约减少了 8%。这直接转化为该特定服务的云账单上的 8%费用节省,使其成为一项有价值的改进。
![5](https://github.com/gocn/translator/blob/master/static/images/2023/w07-Go-1-20-Experiment-Memory-Arenas-vs-Traditional-Memory-Management/5.png)
@@ -100,11 +100,11 @@ Go arenas 目前的一个主要缺点是它是一项实验性特性。 API 和
## 解决社区关注的问题
-Go团队已经收到了关于 arenas 的大量反馈,我们想要回应社区中我们所看到的一些担忧。有关 arenas 最常见的问题是它们添加了一种隐式且不立即显现问题的程序崩溃方式,使语言变得更加复杂。
+Go 团队已经收到了关于 arenas 的大量反馈,我们想要回应社区中我们所看到的一些担忧。有关 arenas 最常见的问题是它们添加了一种隐式且不立即显现问题的程序崩溃方式,使语言变得更加复杂。
大部分的批评是明确的但误导性的。我们不预期 arenas 会变得普遍。我们认为 arenas 是一个强大的工具,但只适用于特定情况。在我们看来, arenas 应该包含在标准库中,但它们的使用应该受到警惕,就像使用`unsafe`,`reflect`或`cgo`一样。
-我们对 arenas 的经验非常充分,我们能够证明 arenas 可以显着减少垃圾回收和内存分配的时间。本文描述的实验关注的是一个单独的、已经高度优化的服务,我们仍然能够通过使用 arenas 获得8%的额外性能。我们认为许多用户可以从在代码库中使用 arenas 中获益更多。
+我们对 arenas 的经验非常充分,我们能够证明 arenas 可以显着减少垃圾回收和内存分配的时间。本文描述的实验关注的是一个单独的、已经高度优化的服务,我们仍然能够通过使用 arenas 获得 8%的额外性能。我们认为许多用户可以从在代码库中使用 arenas 中获益更多。
此外,我们还发现,相比我们过去尝试的其他优化(如使用缓冲池或编写自定义无分配 protobuf 解析器), arenas 的实现更容易。与其他类型的优化相比,它们具有相同的缺点,但提供了更多的好处 - 因此,在我们看来, arenas 是一个净赢。我们希望未来能看到 arenas 成为标准库的一部分(并且是常用包如 protobuf 或 JSON 解析器的一部分)。
@@ -112,4 +112,4 @@ Go团队已经收到了关于 arenas 的大量反馈,我们想要回应社区
Arenas 对于优化 Go 程序是一个强大的工具,特别适用于处理大量 protobuf 或 JSON 块的情况。它们有可能带来显著的性能改进,但是需要注意的是它们是一个实验性的功能,不保证兼容性或在未来版本中的存在。
-我们建议您对应用程序进行分析,并在代码库的有限部分尝试使用arenas,并将您的结果[报告给 Go 团队](https://github.com/golang/go/issues/51317)。
+我们建议您对应用程序进行分析,并在代码库的有限部分尝试使用 arenas,并将您的结果[报告给 Go 团队](https://github.com/golang/go/issues/51317)。
diff --git a/2023/w08_Profile_guided_optimization_prview.md b/2023/w08_Profile_guided_optimization_prview.md
index 097bc27..94c6185 100644
--- a/2023/w08_Profile_guided_optimization_prview.md
+++ b/2023/w08_Profile_guided_optimization_prview.md
@@ -25,14 +25,14 @@ Go 1.20 包含用于预览的 PGO 的初始支持。 完整文档请参阅[按
### 建立开发环境
-```
+```plain
$ go mod init example.com/markdown
$ go get gitlab.com/golang-commonmark/markdown@bf3e522c626a
```
在 `main.go`:
-```
+```plain
package main
import (
@@ -89,7 +89,7 @@ func main() {
构建并运行服务器:
-```
+```plain
$ go build -o markdown.nopgo.exe
$ ./markdown.nopgo.exe
2023/01/19 14:26:24 Serving on port 8080...
@@ -97,7 +97,7 @@ $ ./markdown.nopgo.exe
让我们尝试从另一个终端发送一些 Markdown。我们可以将 Go 项目中的 README 文件作为示例文档:
-```
+```plain
$ curl -o README.md -L "https://raw.githubusercontent.com/golang/go/c16c2c49e2fa98ae551fc6335215fadd62d33542/README.md"
$ curl --data-binary @README.md http://localhost:8080/render
The Go Programming Language
@@ -114,13 +114,13 @@ reliable, and efficient software.
通常,您希望从生产环境中收集性能分析文件,以便编译器获得生产环境中代表性行为。 由于此示例没有“生产”环境,因此我们将创建一个简单的程序来生成负载,以便收集性能分析文件。 复制此[程序](https://go.dev/play/p/yYH0kfsZcpL)的源代码到 `load/main.go` 中并启动负载生成器(确保服务器仍在运行!)。
-```
+```plain
$ go run example.com/markdown/load
```
在负载生成器还在运行中,从服务器上下载一个性能分析文件:
-```
+```plain
$ curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30"
```
@@ -134,7 +134,7 @@ $ curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30"
让我们构建:
-```
+```plain
$ mv cpu.pprof default.pgo
$ go build -pgo=auto -o markdown.withpgo.exe
```
@@ -145,31 +145,31 @@ $ go build -pgo=auto -o markdown.withpgo.exe
首先,我们将对没有 PGO 的服务器进行基准测试。启动该服务器:
-```
+```plain
$ ./markdown.nopgo.exe
```
在服务器运行时,执行一些基准测试迭代:
-```
+```plain
$ go test example.com/markdown/load -bench=. -count=20 -source ../README.md > nopgo.txt
```
完成后,终止原服务器并启动 PGO 版本的服务器:
-```
+```plain
$ ./markdown.withpgo.exe
```
在服务器运行时,执行一些基准测试迭代:
-```
+```plain
$ go test example.com/markdown/load -bench=. -count=20 -source ../README.md > withpgo.txt
```
完成后,我们对比结果:
-```
+```plain
$ go install golang.org/x/perf/cmd/benchstat@latest
$ benchstat nopgo.txt withpgo.txt
goos: linux
diff --git a/2023/w09_Introducing_Service_Weaver_A_Framework_for_Writing_Distributed_Applications.md b/2023/w09_Introducing_Service_Weaver_A_Framework_for_Writing_Distributed_Applications.md
index 32de78f..f78301b 100644
--- a/2023/w09_Introducing_Service_Weaver_A_Framework_for_Writing_Distributed_Applications.md
+++ b/2023/w09_Introducing_Service_Weaver_A_Framework_for_Writing_Distributed_Applications.md
@@ -54,7 +54,7 @@ func main() {
要对上述应用程序进行更改,例如向 Add 方法添加无限数量的参数,您只需要更改 Add 的签名,更改其调用位置,然后重新部署应用程序。Service Weaver 确保新版本的 main() 仅与新版本的 Adder 通信,而不管它们是否共存。这种行为,结合使用语言原生的数据结构和方法调用,使您可以专注于编写应用程序逻辑,而不必担心部署拓扑和服务间通信(例如,在代码中没有 protos、stubs 或 RPC 通道)。
-当运行应用程序时,Service Weaver 允许您在任何地方运行它——在您的本地桌面环境、本地机架或云上——而不需要更改应用程序代码。这种可移植性是通过Service Weaver 框架内置的明确关注点分离实现的。在一端,我们有编程框架,用于应用程序开发。在另一端,我们有各种“**部署器**”实现,每个实现针对一种部署环境。
+当运行应用程序时,Service Weaver 允许您在任何地方运行它——在您的本地桌面环境、本地机架或云上——而不需要更改应用程序代码。这种可移植性是通过 Service Weaver 框架内置的明确关注点分离实现的。在一端,我们有编程框架,用于应用程序开发。在另一端,我们有各种“**部署器**”实现,每个实现针对一种部署环境。
![Flow chart depicting Service Weaver Libraries deployer implementations across three separate platforms in one single iteration](C:\Users\zhengxm\Documents\notes\翻译\Introducing Service Weaver A Framework for Writing Distributed Applications\2.png)
@@ -62,8 +62,8 @@ func main() {
## Service Weaver v0.1 包括什么?
-- 用于编写应用程序的[Go核心库](https://github.com/ServiceWeaver/weaver)。
-- 用于在本地或 GKE 上运行应用程序的一些部署器,如[本地部署器](https://github.com/ServiceWeaver/weaver/tree/main/cmd/weaver)或[ GKE 部署器](https://github.com/ServiceWeaver/weaver-gke)。
+- 用于编写应用程序的[Go 核心库](https://github.com/ServiceWeaver/weaver)。
+- 用于在本地或 GKE 上运行应用程序的一些部署器,如[本地部署器](https://github.com/ServiceWeaver/weaver/tree/main/cmd/weaver)或[GKE 部署器](https://github.com/ServiceWeaver/weaver-gke)。
- 一组 API,允许您为任何其他平台编写自己的部署器。
所有库都在 Apache 2.0 许可下发布。请注意,在发布 v1.0 版本之前,**我们可能会引入破坏性更改**。
@@ -79,6 +79,6 @@ func main() {
## 更多资源
- 访问我们的网站[serviceweaver.dev](https://serviceweaver.dev/),获取有关该项目的最新信息,例如入门指南、教程和博客文章。
-- 访问我们在GitHub上的其中一个Service Weaver [代码库](https://github.com/orgs/ServiceWeaver/repositories)。
+- 访问我们在 GitHub 上的其中一个 Service Weaver [代码库](https://github.com/orgs/ServiceWeaver/repositories)。
*By Srdjan Petrovic and Garv Sawhney, 仅代表 Service Weaver team*
diff --git a/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know.md b/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know.md
index 624a12e..4db7cd7 100644
--- a/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know.md
+++ b/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know.md
@@ -1,4 +1,4 @@
-# Go性能加速器:你需要知道的5个诀窍和技巧
+# Go 性能加速器:你需要知道的 5 个诀窍和技巧
- 原文地址:
- 原文作者:Aiden (@func25)
@@ -6,16 +6,16 @@
- 译者:[小超人](https://github.com/focozz)
- 校对:[784909593](https://github.com/784909593)
-> 通过这 5个诀窍和技巧来将那些运行缓慢,低效的 go 代码变成精简,高效,快速的机器代码。
+> 通过这 5 个诀窍和技巧来将那些运行缓慢,低效的 go 代码变成精简,高效,快速的机器代码。
![Go性能加速器:你需要知道的5个诀窍和技巧](../static/images/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know/1_7pKdwB_c5boKS_235L9DRQ.png)
-Go性能加速器:你需要知道的5个诀窍和技巧
+Go 性能加速器:你需要知道的 5 个诀窍和技巧
各位 Go 大师和初学者们,你们是否已经厌倦了那些慢得让你想要抓狂的 Go 应用程序?别担心,我们有解决方案。
-在这篇文章中,我将分享将 Go 应用程序变成精简、高效的前5个诀窍和技巧。
+在这篇文章中,我将分享将 Go 应用程序变成精简、高效的前 5 个诀窍和技巧。
所以拿杯咖啡,放松一下,准备把你的 Go 技能提升到更高的水平。
-## 1. 避免使用反射。
+## 1. 避免使用反射
反射是 Go 中一个强大的特性,它允许程序在运行时自我检查和修改自身的结构和行为。
你可以使用反射来确定一个值的类型,访问其字段,并调用其方法。
@@ -39,10 +39,10 @@ func main() {
```
**但是!** 反射是在运行时进行值的自我检查和操作,而不是编译时。
-Go运行时必须执行额外的工作来确定反射值的类型和结构,这可能会增加开销并减慢程序速度。
+Go 运行时必须执行额外的工作来确定反射值的类型和结构,这可能会增加开销并减慢程序速度。
反射还会使代码更难以阅读和理解而使影响生产力受到影响。
-## 2. 避免使用字符串拼接。
+## 2. 避免使用字符串拼接
通常使用 `bytes.Buffer` 类型来构建字符串比使用 `+` 操作符连接字符串更有效率。
@@ -94,19 +94,19 @@ fmt.Println(s)
我已经比较了这两种解决方案,结果如下:
- 使用 `bytes.Buffer` 比使用字符串拼接快得多,在某些情况下性能提升超过 250 倍。
-- 使用 `strings.Builder` 大约比 `bytes.Buffer` 快1.5倍。
+- 使用 `strings.Builder` 大约比 `bytes.Buffer` 快 1.5 倍。
需要注意的是,实际的性能提升可能因为特定的 CPU 和代码运行环境等因素而有所差异。
-> strings.Builder比bytes.Buffer更快的原因有几个。
+> strings.Builder 比 bytes.Buffer 更快的原因有几个。
这是因为 `strings.Builder` 专门针对字符串的构建进行了优化。相比之下,`bytes.Buffer` 是一个更通用的缓冲区,可以用于构建任何类型的数据,但它可能没有像 `strings.Builder` 那样专门优化字符串构建的性能。
-## 3. 预分配切片和 map 的空间。
+## 3. 预分配切片和 map 的空间
-在Go中,为预期容纳的元素数量适当分配切片的容量可以提高性能。
+在 Go 中,为预期容纳的元素数量适当分配切片的容量可以提高性能。
这是因为分配具有更大容量的切片可以减少在添加元素时需要调整切片大小的次数。
@@ -133,11 +133,11 @@ func main() {
```
-是的,通过预分配,我们能够将速度提升3倍。
+是的,通过预分配,我们能够将速度提升 3 倍。
我已经在一篇关于切片的文章中对于[为什么预分配更快](https://medium.com/@func25/go-secret-slice-a-deep-dive-into-slice-6bd7b0b70ec4)写了一个详细的解释,你可以直接点击链接查看。
-## 4. 避免使用只有一个具体类型的接口。
+## 4. 避免使用只有一个具体类型的接口
如果你知道一个接口只会有一个具体类型,你可以直接使用该具体类型,以避免接口的开销。
@@ -166,7 +166,7 @@ func main() {
```
-使用接口的耗时为358微秒,而使用具体类型的耗时为342微秒。
+使用接口的耗时为 358 微秒,而使用具体类型的耗时为 342 微秒。
需要注意的是,只有当你确信一个接口只会有一个具体类型时,才应该使用这种技术。
diff --git a/2023/w15_Building_Basic_Event_Scheduler_in_Go.md b/2023/w15_Building_Basic_Event_Scheduler_in_Go.md
index 365b94f..159d951 100644
--- a/2023/w15_Building_Basic_Event_Scheduler_in_Go.md
+++ b/2023/w15_Building_Basic_Event_Scheduler_in_Go.md
@@ -4,7 +4,7 @@
- 译者:[lsj1342](https://github.com/lsj1342)
- 校对:[cvley](https://github.com/cvley)
-## Go构建基础的事件调度器
+## Go 构建基础的事件调度器
![](https://github.com/gocn/translator/raw/master/static/images/2023/w15_Building_Basic_Event_Scheduler_in_Go//1_gBs7tyig8N5eeHNMOwIG8w.webp)
当我们需要在一段时间后的特定时间或间隔运行任务时,我们需要使用任务调度系统来运行任务:例如发送电子邮件、推送通知、午夜关闭账户、清空表格等
@@ -66,7 +66,7 @@ func NewScheduler(db *sql.DB, listeners Listeners) Scheduler {
}
```
-在第 8 行到第 13 行中,我们通过将sql.DB实例和初始侦听器传递给调度程序来创建新的调度程序。
+在第 8 行到第 13 行中,我们通过将 sql.DB 实例和初始侦听器传递给调度程序来创建新的调度程序。
现在,我们实现调度函数,并将我们的事件插入到 `jobs` 表中;
```go
@@ -200,12 +200,12 @@ func main() {
在第 13 行到第 16 行中,我们将侦听函数绑定到事件 `SendEmail` 和 `PayBills`上,以便在发生新事件时调用这些函数。
-在 22行 和 32 到 37 行中,我们添加了中断信号(os.Interrupt)通道,当程序中发生中断时,我们执行 19 行中的上下文取消函数。
+在 22 行 和 32 到 37 行中,我们添加了中断信号(os.Interrupt)通道,当程序中发生中断时,我们执行 19 行中的上下文取消函数。
从第 26 行到第 30 行,我们定义事件调度程序、运行轮询函数并将在一分钟后运行 `SendEmail` ,两分钟后运行 `PayBills`。
程序的输出将如下所示;
-```
+```plain
2021/01/16 11:58:49 💾 Seeding database with table...
2021/01/16 11:58:49 🚀 Scheduling event SendEmail to run at 2021-01-16 11:59:49.344904505 +0545 +0545 m=+60.004623549
diff --git a/2023/w17_Dont_write_clean_code_write_CRISP_code.md b/2023/w17_Dont_write_clean_code_write_CRISP_code.md
index 877bbb5..ab32902 100644
--- a/2023/w17_Dont_write_clean_code_write_CRISP_code.md
+++ b/2023/w17_Dont_write_clean_code_write_CRISP_code.md
@@ -20,7 +20,7 @@
你的代码可能有很多其他优点,但如果它不是*正确的*,那么这些优点实际上并不重要。而且,如果我们愿意放弃正确性,我们可以相对容易地获得任何其他属性。在编写代码时,正确性是最重要的因素之一,因为一个程序只有在它能够正确地完成它的任务时才有意义。
->*正确性是首要的品质。如果一个系统不能够完成它应该完成的任务,那么它的其他方面——无论它有多快,有多好的用户界面——都不重要。但这说起来容易做起来难*
+> *正确性是首要的品质。如果一个系统不能够完成它应该完成的任务,那么它的其他方面——无论它有多快,有多好的用户界面——都不重要。但这说起来容易做起来难*
>*——Bertrand Meyer,《面向对象软件构造》*
虽然这个观点似乎很显然,不值得一提,但如果存在不正确的代码,而我认为确实存在,那么这个观点肯定值得强调,因为显然有人没有注意到这个重要性。
diff --git a/2023/w19_Go_Developer_Survey_2023_Q1_Results.md b/2023/w19_Go_Developer_Survey_2023_Q1_Results.md
index 4d61e90..0d89ffc 100644
--- a/2023/w19_Go_Developer_Survey_2023_Q1_Results.md
+++ b/2023/w19_Go_Developer_Survey_2023_Q1_Results.md
@@ -1,4 +1,4 @@
-# 2023年第一季度 Go 开发者调查结果
+# 2023 年第一季度 Go 开发者调查结果
- 原文地址:https://go.dev/blog/survey2023-q1-results
- 原文作者:***Alice Merrick***
@@ -8,41 +8,41 @@
## 感谢调查参与者给我们带来了这些洞察!
-我们非常高兴与您分享2023年1月份 Go 开发者调查的结果。感谢 5844 位回应者与我们分享他们如何使用 Go,他们在使用 Go 时面临的最大挑战,以及他们对未来改进的首要课题。这些结果帮助 Go 团队将我们的工作重点放在社区最关心的领域,我们希望这些洞察也能帮助其他为 Go 生态系统做出贡献和支持的人了解相关信息。
+我们非常高兴与您分享 2023 年 1 月份 Go 开发者调查的结果。感谢 5844 位回应者与我们分享他们如何使用 Go,他们在使用 Go 时面临的最大挑战,以及他们对未来改进的首要课题。这些结果帮助 Go 团队将我们的工作重点放在社区最关心的领域,我们希望这些洞察也能帮助其他为 Go 生态系统做出贡献和支持的人了解相关信息。
### 主要发现
- **新手 Go 开发者对 Web 开发感兴趣。**我们今年新引入了一个基于自我识别的经验水平的划分。新手表达出了一些与其他经验级别相比的有趣差异。最值得注意的是他们对使用 Go 进行 web 开发的兴趣更大。
-- **错误处理和学习是参与者面临的最大挑战。**历史上,缺乏泛型是使用Go的最大挑战,但自从引入泛型后,我们看到关于泛型的评论有所下降。现在最常被报告的挑战是关于错误处理(与可读性和冗长性有关)和学习最佳实践的困难。
+- **错误处理和学习是参与者面临的最大挑战。**历史上,缺乏泛型是使用 Go 的最大挑战,但自从引入泛型后,我们看到关于泛型的评论有所下降。现在最常被报告的挑战是关于错误处理(与可读性和冗长性有关)和学习最佳实践的困难。
- **优化指南被认为是最有价值的提升 Go 性能的方式。**当被问及如何在 Go 的编译和运行时的各种改进上投入资源时,参与者最多的选择是优化指南,而不是具体的性能改进,这证明了在这一领域,文档的价值有多大。
- **管理依赖和版本是开源 Go 模块维护者面临的最大挑战。**开源模块维护者在保持他们的依赖关系更新和避免因版本和突发改变引起的中断方面面临挑战。这是我们将进一步探索的领域,以帮助维护者提供稳定和健康的生态系统。
### 如何阅读调查结果
-在这篇文章中,我们使用调查问卷的图表来为我们的发现提供证据支持。所有这些图表都使用类似的格式。标题是调查参与者看到的确切问题。除非另有说明,问题都是选择题,参与者只能选择一个答案;每个图表的子标题将告诉你问题是否允许多个答案,或者是开放式文本框而不是选择题。对于开放式文本回答的图表,Go 团队的成员阅读并手动分类了所有的回答。许多开放式问题引发了各种各样的回答;为了保持图表的合理大小,我们将它们压缩到了前10-15个主题,其他的主题都归类为“其他”。在适用的地方,我们还包含了一个“无”的类别。
+在这篇文章中,我们使用调查问卷的图表来为我们的发现提供证据支持。所有这些图表都使用类似的格式。标题是调查参与者看到的确切问题。除非另有说明,问题都是选择题,参与者只能选择一个答案;每个图表的子标题将告诉你问题是否允许多个答案,或者是开放式文本框而不是选择题。对于开放式文本回答的图表,Go 团队的成员阅读并手动分类了所有的回答。许多开放式问题引发了各种各样的回答;为了保持图表的合理大小,我们将它们压缩到了前 10-15 个主题,其他的主题都归类为“其他”。在适用的地方,我们还包含了一个“无”的类别。
为了帮助读者理解每项发现背后的证据的权重,我们在图表中包含了显示回答的 95% 置信区间的误差条;条状越窄,置信度越大。有时,两个或更多的回答的误差条可能会重叠,这意味着这些回答的相对顺序在统计上没有意义(也就是说,这些回答实际上是平手的)。每个图表的右下角显示了在图表中包含的人数,以“n = [回答者人数]”的形式。
### 关于方法论的说明
-大多数调查回答者通过在 [Go 博客](https://go.dev/blog)、[@golang推特](https://twitter.com/golang)或其他 Go 社交渠道上的链接“自我选择”参加了这次调查。那些不关注这些渠道的人可能会与那些密切关注这些渠道的人有不同的回答。大约四分之一的回答者是随机抽样的,也就是说他们在 VS Code 中看到调查提示后回答了调查(在2023年1月18日至2月8日期间使用VS Code Go插件的每个人都有10%的机会收到这个随机提示)。这个随机抽样的群体帮助我们将这些发现推广到更大的 Go 开发者社区。大多数调查问题在这两个群体之间没有显示出有意义的差异,但在少数有重要差异的情况下,读者会看到将回答分为“随机样本”和“自选样本”群体的图表。
+大多数调查回答者通过在 [Go 博客](https://go.dev/blog)、[@golang 推特](https://twitter.com/golang)或其他 Go 社交渠道上的链接“自我选择”参加了这次调查。那些不关注这些渠道的人可能会与那些密切关注这些渠道的人有不同的回答。大约四分之一的回答者是随机抽样的,也就是说他们在 VS Code 中看到调查提示后回答了调查(在 2023 年 1 月 18 日至 2 月 8 日期间使用 VS Code Go 插件的每个人都有 10%的机会收到这个随机提示)。这个随机抽样的群体帮助我们将这些发现推广到更大的 Go 开发者社区。大多数调查问题在这两个群体之间没有显示出有意义的差异,但在少数有重要差异的情况下,读者会看到将回答分为“随机样本”和“自选样本”群体的图表。
## 深入研究不同群体的回答者
-我们的受访者人口统计数据与[上次调查](https://go.dev/blog/survey2022-q2-results)相比没有显著变化。与以往的周期一致,Go 主要用于技术行业,约80%的受访者表示他们在工作中使用 Go 编程。总体而言,调查受访者在过去一年中对 Go 感到满意,92%的人表示他们在某种程度上或非常满意。
+我们的受访者人口统计数据与[上次调查](https://go.dev/blog/survey2022-q2-results)相比没有显著变化。与以往的周期一致,Go 主要用于技术行业,约 80%的受访者表示他们在工作中使用 Go 编程。总体而言,调查受访者在过去一年中对 Go 感到满意,92%的人表示他们在某种程度上或非常满意。
![Bar chart
showing where respondents use Go](https://go.dev/blog/survey2023q1/where.svg) ![Bar chart showing proportion of satisfied
respondents](https://go.dev/blog/survey2023q1/csat.svg)
-相比其他语言,我们的受访者花费了很多时间在 Go 编程上。大约三分之一的受访者甚至维护着一个开源的 Go 模块。我们认识到我们的调查对象是那些成功采用Go、经常使用 Go 并且大多数人使用 Go 都感到满意的人。为了确定满足社区需求的潜在差距,我们查看不同的受访者子群体,看看他们可能如何不同地使用 Go 或有不同的优先事项。例如,今年我们查看了不同样本来源(即 Go 博客或通过VS Code插件)、不同的工作角色、组织规模和 Go 经验水平之间的差异。最有趣的差异在于经验水平之间。
+相比其他语言,我们的受访者花费了很多时间在 Go 编程上。大约三分之一的受访者甚至维护着一个开源的 Go 模块。我们认识到我们的调查对象是那些成功采用 Go、经常使用 Go 并且大多数人使用 Go 都感到满意的人。为了确定满足社区需求的潜在差距,我们查看不同的受访者子群体,看看他们可能如何不同地使用 Go 或有不同的优先事项。例如,今年我们查看了不同样本来源(即 Go 博客或通过 VS Code 插件)、不同的工作角色、组织规模和 Go 经验水平之间的差异。最有趣的差异在于经验水平之间。
## 来自新手调查对象的洞察
![Bar chart of years of experience using
Go](https://go.dev/blog/survey2023q1/go_exp.svg)
-过去,我们以回答者使用 Go 的时间(以月/年为单位)为根据,以洞察不同经验级别的结果如何变化。今年我们尝试了一个新的分段问题,“你的Go经验级别是什么?”,看看自我认定相比将各种时间间隔混合在一起,是否可能是一种检查 Go 经验更有用的方式。由于像“初学者”或“专家”这样的分类术语可能因人而异,我们提供了描述以帮助使这些分类更具客观性。选项包括:
+过去,我们以回答者使用 Go 的时间(以月/年为单位)为根据,以洞察不同经验级别的结果如何变化。今年我们尝试了一个新的分段问题,“你的 Go 经验级别是什么?”,看看自我认定相比将各种时间间隔混合在一起,是否可能是一种检查 Go 经验更有用的方式。由于像“初学者”或“专家”这样的分类术语可能因人而异,我们提供了描述以帮助使这些分类更具客观性。选项包括:
- 了解:我知道 Go,但是如果没有帮助,我不能写一个简单的 Go 程序
- 初学者:我可以在可能需要帮助的情况下完成 Go 的简单编程项目
@@ -55,45 +55,45 @@ using Go](https://go.dev/blog/survey2023q1/exp_level.svg)
我们发现回答者使用 Go 的时间长短与他们自我认定的经验等级之间存在中等程度的相关性(⍴ = .66)。这意味着,虽然经验等级尺度与时间尺度类似,但它可能会给我们提供一些关于不同经验等级的回答者之间的差异的新洞见。例如,一个回答者花在 Go 编程上的时间比例与他们花在其他语言编程上的时间比较,与他们自我认定的经验等级的相关性比他们使用 Go 的时间长短的相关性更强。
-在我们使用这种分段的分析中,我们通常会排除“了解”类别,因为他们被认为没有回答问题所必需的经验,并且只占回答者的约1%。
+在我们使用这种分段的分析中,我们通常会排除“了解”类别,因为他们被认为没有回答问题所必需的经验,并且只占回答者的约 1%。
-### 初学回答者更可能比更有经验的回答者更喜欢Windows
+### 初学回答者更可能比更有经验的回答者更喜欢 Windows
-我们随机抽样的群体中,初学者的比例比自选群体高,这表明我们不常听到的新 Gopher 更多。因为他们是通过 Go VS Code 插件进行抽样的,我们可能期望这个群体更喜欢使用 VS Code 或比其他经验等级更喜欢在 Windows 上开发。虽然这是真的,但无论他们是否通过VS Code 插件回答,初学者也更可能在 Windows 上开发。
+我们随机抽样的群体中,初学者的比例比自选群体高,这表明我们不常听到的新 Gopher 更多。因为他们是通过 Go VS Code 插件进行抽样的,我们可能期望这个群体更喜欢使用 VS Code 或比其他经验等级更喜欢在 Windows 上开发。虽然这是真的,但无论他们是否通过 VS Code 插件回答,初学者也更可能在 Windows 上开发。
![Bar chart of levels of experience
using Go for self-selected and random samples](https://go.dev/blog/survey2023q1/exp_level_s.svg) ![Bar chart of editor
preference broken down by experience levels for self-selected group only](https://go.dev/blog/survey2023q1/editor_self_select_exp.svg) ![Bar chart of
levels of experience using Go](https://go.dev/blog/survey2023q1/os_dev_exp_s.svg)
-我们在更高的经验等级中看不到更多 Windows 用户的比例,可能有多种原因。例如,Windows用户可能更容易遇到困难并停止使用Go,或者可能存在与 Go 无关的操作系统使用的更广泛的趋势。无论如何,我们应该在未来关于开始使用 Go 的研究中包括更多的Windows 用户,以确保我们提供一个包容性的入门体验。
+我们在更高的经验等级中看不到更多 Windows 用户的比例,可能有多种原因。例如,Windows 用户可能更容易遇到困难并停止使用 Go,或者可能存在与 Go 无关的操作系统使用的更广泛的趋势。无论如何,我们应该在未来关于开始使用 Go 的研究中包括更多的 Windows 用户,以确保我们提供一个包容性的入门体验。
-### 不同经验等级的用户当前如何使用Go(以及他们希望使用的其他领域)
+### 不同经验等级的用户当前如何使用 Go(以及他们希望使用的其他领域)
![Bar chart of use cases](https://go.dev/blog/survey2023q1/go_app.svg) ![Bar chart of use
cases broken down by experience level](https://go.dev/blog/survey2023q1/go_app_exp.svg)
-根据调查者目前如何使用Go,更有经验的 Gopher 倾向于使用 Go 进行更多类型的应用。例如,平均每个专家至少在四个领域使用Go,而平均每个初学者只在两个领域使用Go。这就是为什么在每个使用案例中,使用 Go 的初学者和专家的比例有很大的差异。然而,前两大应用,API / RPC服务和CLI,都是所有经验等级的主要用例。
+根据调查者目前如何使用 Go,更有经验的 Gopher 倾向于使用 Go 进行更多类型的应用。例如,平均每个专家至少在四个领域使用 Go,而平均每个初学者只在两个领域使用 Go。这就是为什么在每个使用案例中,使用 Go 的初学者和专家的比例有很大的差异。然而,前两大应用,API / RPC 服务和 CLI,都是所有经验等级的主要用例。
-我们在 GUI 和 Website / Web 服务(返回HTML)中看到更有趣的趋势。所有经验等级都以大约相同的比例使用 Go 进行桌面/ GUI 应用。这为我们提供了证据,GUI 的需求并不仅仅来自寻找有趣的入门项目的新 Gopher,而是来自整个经验谱。
+我们在 GUI 和 Website / Web 服务(返回 HTML)中看到更有趣的趋势。所有经验等级都以大约相同的比例使用 Go 进行桌面/ GUI 应用。这为我们提供了证据,GUI 的需求并不仅仅来自寻找有趣的入门项目的新 Gopher,而是来自整个经验谱。
-返回 HTML 的 Website / Web 服务显示出类似的趋势。一种解释可能是这是某人开始 Go 之旅早期的一个常见用例(因为它是初学者中最常用的前3个),或者初学者更可能在返回 HTML 的 Website / Web 服务上工作。在调查的后期,我们问回答者,“在哪个领域(如果有的话)你没有使用 Go,但最想使用?”虽然许多回答者(29%)表示他们已经在他们希望的地方使用Go,但扩大使用的前两个领域是GUI / 桌面和AI / ML应用。这在不同组织规模和工作角色的群体中保持一致,但在经验等级上并非如此。初学者最想更多使用 Go的领域是网站/返回HTML的 websites / web 服务 。
+返回 HTML 的 Website / Web 服务显示出类似的趋势。一种解释可能是这是某人开始 Go 之旅早期的一个常见用例(因为它是初学者中最常用的前 3 个),或者初学者更可能在返回 HTML 的 Website / Web 服务上工作。在调查的后期,我们问回答者,“在哪个领域(如果有的话)你没有使用 Go,但最想使用?”虽然许多回答者(29%)表示他们已经在他们希望的地方使用 Go,但扩大使用的前两个领域是 GUI / 桌面和 AI / ML 应用。这在不同组织规模和工作角色的群体中保持一致,但在经验等级上并非如此。初学者最想更多使用 Go 的领域是网站/返回 HTML 的 websites / web 服务 。
![Bar chart of levels of
experience using Go](https://go.dev/blog/survey2023q1/app_opportunities_exp.svg)
-在一个开放式文本问题中,有29位回答者表示他们希望使用 Go 进行返回HTML的 websites / web 服务,其中12位表示他们被阻碍了,因为其他语言有更好支持这个用例的框架。更有经验的 Go 开发者可能在其他语言已经有满足这些需求的框架时,不会尝试或期望使用Go进行这个用例。正如一位回答者所说,
+在一个开放式文本问题中,有 29 位回答者表示他们希望使用 Go 进行返回 HTML 的 websites / web 服务,其中 12 位表示他们被阻碍了,因为其他语言有更好支持这个用例的框架。更有经验的 Go 开发者可能在其他语言已经有满足这些需求的框架时,不会尝试或期望使用 Go 进行这个用例。正如一位回答者所说,
> “通常在其他语言如 PHP 或 Ruby 中实现这个更容易。部分原因是这些语言中存在的优秀框架。”
-初学者对 web 开发的兴趣可能与他们使用 JavaScript / TypeScript 的情况有关。初学者花费在 JavaScript / TypeScript 上的时间比更有经验的回答者多。对 web 的高度兴趣可能与初学者目前在其他语言上工作的内容有关,或者可能仅仅表明对 web 技术感兴趣。在未来,我们希望更多地了解这个用例,以及我们如何可以帮助新的 Gopher 开始在对他们最有用的领域使用Go。
+初学者对 web 开发的兴趣可能与他们使用 JavaScript / TypeScript 的情况有关。初学者花费在 JavaScript / TypeScript 上的时间比更有经验的回答者多。对 web 的高度兴趣可能与初学者目前在其他语言上工作的内容有关,或者可能仅仅表明对 web 技术感兴趣。在未来,我们希望更多地了解这个用例,以及我们如何可以帮助新的 Gopher 开始在对他们最有用的领域使用 Go。
![Bar chart of levels of
experience using Go](https://go.dev/blog/survey2023q1/language_time_exp.svg)
## 调查者面临一系列长期挑战
-在每个调查周期,我们都会询问回应者在使用 Go 时最大的挑战是什么。从历史来看,缺乏泛型是最常被引用的挑战——例如,这是2020年最常见的回应,约18%的回答者提到了这一点。自从引入了泛型以来,错误处理(12%)和学习/最佳实践/文档(11%)已经在一系列长期问题中脱颖而出,而不是任何单一问题变得更加频繁。
+在每个调查周期,我们都会询问回应者在使用 Go 时最大的挑战是什么。从历史来看,缺乏泛型是最常被引用的挑战——例如,这是 2020 年最常见的回应,约 18%的回答者提到了这一点。自从引入了泛型以来,错误处理(12%)和学习/最佳实践/文档(11%)已经在一系列长期问题中脱颖而出,而不是任何单一问题变得更加频繁。
![Bar chart of
biggest challenges](https://go.dev/blog/survey2023q1/text_biggest_challenge.svg)
@@ -108,13 +108,13 @@ biggest challenges](https://go.dev/blog/survey2023q1/text_biggest_challenge.svg)
### 学习最佳实践的挑战
-> “有效地使用Go。容易学习,难以掌握。”
+> “有效地使用 Go。容易学习,难以掌握。”
-我们听到Go语言易于学习,而且[之前的一项调查显示](https://go.dev/blog/survey2020-results#TOC_6.2),超过70%的回应者在第一年内使用 Go 就感到了效率,但是学习 Go 的最佳实践被提出作为使用 Go 的最大挑战之一。今年的回应者告诉我们,关于**代码结构**和**推荐工具和库**的最佳实践文档不足,这给初学者和团队保持代码一致性带来了挑战。对于来自其他编程范式的人来说,学习写符合惯用方式的 Go 尤其具有挑战性。对 Go 经验更丰富的回应者证实,当开发者不遵循编写符合惯用方式的 Go 的最佳实践时,这会损害共享项目的一致性和质量。
+我们听到 Go 语言易于学习,而且[之前的一项调查显示](https://go.dev/blog/survey2020-results#TOC_6.2),超过 70%的回应者在第一年内使用 Go 就感到了效率,但是学习 Go 的最佳实践被提出作为使用 Go 的最大挑战之一。今年的回应者告诉我们,关于**代码结构**和**推荐工具和库**的最佳实践文档不足,这给初学者和团队保持代码一致性带来了挑战。对于来自其他编程范式的人来说,学习写符合惯用方式的 Go 尤其具有挑战性。对 Go 经验更丰富的回应者证实,当开发者不遵循编写符合惯用方式的 Go 的最佳实践时,这会损害共享项目的一致性和质量。
### 模块维护者面临的最大挑战
-Go模块维护者是 Go 社区的关键成员,帮助发展并维持我们的包生态系统的健康。今年我们计划与模块维护者进行研究,以确定支持包生态系统的稳定性和增长,以及寻找在组织内推广 Go 的机会。为了为这项研究提供信息,我们在调查中引入了一个问题,以了解当前开源维护者面临的最大挑战。
+Go 模块维护者是 Go 社区的关键成员,帮助发展并维持我们的包生态系统的健康。今年我们计划与模块维护者进行研究,以确定支持包生态系统的稳定性和增长,以及寻找在组织内推广 Go 的机会。为了为这项研究提供信息,我们在调查中引入了一个问题,以了解当前开源维护者面临的最大挑战。
![Bar chart of
challenges for open source module maintainers](https://go.dev/blog/survey2023q1/text_maintainer_challenge.svg)
@@ -130,7 +130,7 @@ for open source module maintainers](https://go.dev/blog/survey2023q1/text_deploy
## 社区优先级:受访者最关心的问题
-今年我们使用了一种在以往调查中使用过的基于 buy-a-feature 优先级问题。我们给予受访者10个“gopher币”,并让他们将这些币分配给他们希望看到改进的领域。受访者被随机分配三个可能的问题,来自于七个与工具、安全或编译器和运行时相关的项目。这种方法让我们能够在不让受访者承受三组认知要求高的优先级问题的负担的情况下,询问每个重点领域相关的项目。
+今年我们使用了一种在以往调查中使用过的基于 buy-a-feature 优先级问题。我们给予受访者 10 个“gopher 币”,并让他们将这些币分配给他们希望看到改进的领域。受访者被随机分配三个可能的问题,来自于七个与工具、安全或编译器和运行时相关的项目。这种方法让我们能够在不让受访者承受三组认知要求高的优先级问题的负担的情况下,询问每个重点领域相关的项目。
在这个练习的最后,我们给了受访者一个开放文本提示,让他们告诉我们他们认为在明年 Go 团队应该优先考虑的任何领域,无论他们在哪些项目上花费了他们的币。例如,如果一个受访者被展示了安全部分,但他们对安全并不是很关心,他们仍然有机会在开放文本区域中告诉我们这一点。
@@ -141,8 +141,8 @@ for open source module maintainers](https://go.dev/blog/survey2023q1/text_deploy
* pkg.go.dev 识别出维护不良的包(例如,对问题无反应,未能更新其依赖关系,长期保持易受攻击)
* pkg.go.dev 识别出进行了破坏性 API 更改的包(即,当升级这些包到新版本时,需要修复使用这些 API 的地方)
* 支持在 govulncheck 中抑制漏洞
-* 跟踪敏感数据如何流经 Go 程序的工具(检测PII泄露)
-* 安全最佳实践指南(例如,如何选择和更新依赖项;如何设置模糊测试、漏洞检查和线程消毒器;如何使用crypto)
+* 跟踪敏感数据如何流经 Go 程序的工具(检测 PII 泄露)
+* 安全最佳实践指南(例如,如何选择和更新依赖项;如何设置模糊测试、漏洞检查和线程消毒器;如何使用 crypto)
* 默认安全的 Web 和 SQL 库,帮助用户避免在 web 服务器代码中引入漏洞
* 符合 FIPS-140 标准的密码学库
@@ -155,13 +155,13 @@ respondents spent the most on security issues](https://go.dev/blog/survey2023q1/
我们在这个问题中包含的项目是受 VS Code 插件用户反馈的启发。我们想知道哪些工具和 IDE 改进对可能使用其他 IDE 或编辑器的更广泛的受众最有帮助。
-* 更好的重构工具(例如,支持自动代码转换:重命名,函数提取,API迁移等)
-* 在你的代码编辑器/IDE 中更好地支持测试(例如,健壮且可扩展的Test Explorer UI,第三方测试框架,子测试支持,代码覆盖率)
-* 在你的代码编辑器/IDE中更好地支持多模块工作(例如,编辑模块A和B,其中模块A依赖于模块B)
-* 在pkg.go.dev中的依赖性洞察(例如,漏洞,破坏性变化,评分卡)
-* 在你的代码编辑器/IDE中支持依赖性洞察(例如,漏洞,破坏性变化,评分卡)
+* 更好的重构工具(例如,支持自动代码转换:重命名,函数提取,API 迁移等)
+* 在你的代码编辑器/IDE 中更好地支持测试(例如,健壮且可扩展的 Test Explorer UI,第三方测试框架,子测试支持,代码覆盖率)
+* 在你的代码编辑器/IDE 中更好地支持多模块工作(例如,编辑模块 A 和 B,其中模块 A 依赖于模块 B)
+* 在 pkg.go.dev 中的依赖性洞察(例如,漏洞,破坏性变化,评分卡)
+* 在你的代码编辑器/IDE 中支持依赖性洞察(例如,漏洞,破坏性变化,评分卡)
* 支持使用新的模块路径发布模块(例如,仓库所有权移交)
-* 在你的代码编辑器/IDE中支持找到实现了一个接口的类型以及由一个类型实现的接口
+* 在你的代码编辑器/IDE 中支持找到实现了一个接口的类型以及由一个类型实现的接口
![Bar chart of where
respondents spent the most on tooling](https://go.dev/blog/survey2023q1/prioritization_tooling.svg)
@@ -170,29 +170,29 @@ respondents spent the most on tooling](https://go.dev/blog/survey2023q1/prioriti
### 编译器和运行时
-我们在这一部分的关键问题是,想要确定受访者是希望默认有更好的性能,更好的优化工具,还是只是希望更好地理解如何编写高性能的Go 代码。
+我们在这一部分的关键问题是,想要确定受访者是希望默认有更好的性能,更好的优化工具,还是只是希望更好地理解如何编写高性能的 Go 代码。
* 降低计算成本
* 减少内存使用
* 减少二进制文件大小
* 减少构建时间
* 更好的性能调试工具
-* 优化指南(如何提高性能和降低成本,包括Go的实现和性能调试工具)
-* 在交叉编译时更好地支持使用cgo
+* 优化指南(如何提高性能和降低成本,包括 Go 的实现和性能调试工具)
+* 在交叉编译时更好地支持使用 cgo
![Bar chart of where
respondents spent the most on compiler and runtime improvements](https://go.dev/blog/survey2023q1/prioritization_core.svg)
-这个列表中得到最多资金支持的是优化指南。这在组织规模、工作角色和经验水平上都是一致的。我们还问了一个关于受访者是否对资源成本有所担忧的额外问题。大多数受访者(55%)表示他们没有任何成本担忧,但是那些对资源成本有所担忧的人在降低计算成本和内存成本上花费了更多的"gophercoins"(平均为2.0),比那些没有担忧的人多。然而,即使是那些对资源成本有所担忧的人,他们在优化指南上的花费也差不多(平均为1.9个"gophercoins")。这是一个强烈的信号,提供指导 Go 开发者理解和优化 Go 性能的指南,目前比额外的编译器和运行时性能改进更有价值。
+这个列表中得到最多资金支持的是优化指南。这在组织规模、工作角色和经验水平上都是一致的。我们还问了一个关于受访者是否对资源成本有所担忧的额外问题。大多数受访者(55%)表示他们没有任何成本担忧,但是那些对资源成本有所担忧的人在降低计算成本和内存成本上花费了更多的"gophercoins"(平均为 2.0),比那些没有担忧的人多。然而,即使是那些对资源成本有所担忧的人,他们在优化指南上的花费也差不多(平均为 1.9 个"gophercoins")。这是一个强烈的信号,提供指导 Go 开发者理解和优化 Go 性能的指南,目前比额外的编译器和运行时性能改进更有价值。
## 总结
-感谢你加入我们一起回顾2023年第一次开发者调查的结果!理解开发者的经验和挑战有助于我们确定如何最好地服务 Go 社区。我们发现的一些特别有用的收获包括:
+感谢你加入我们一起回顾 2023 年第一次开发者调查的结果!理解开发者的经验和挑战有助于我们确定如何最好地服务 Go 社区。我们发现的一些特别有用的收获包括:
-* Go初学者对网页开发有更高的亲和力,比其他经验水平的受访者更为如此。这是我们希望进一步探索的一个领域,以确保我们满足新的Go开发者的需求。
-* 默认安全、安全和优化的最佳实践指导,以及在IDE中更多的重构帮助,将是对社区的有价值的补充。
-* 错误处理是社区的高优先级问题,它在冗长性和可调试性上带来了挑战。Go团队目前没有公开提案,但正在继续探索改进错误处理的选项。
+* Go 初学者对网页开发有更高的亲和力,比其他经验水平的受访者更为如此。这是我们希望进一步探索的一个领域,以确保我们满足新的 Go 开发者的需求。
+* 默认安全、安全和优化的最佳实践指导,以及在 IDE 中更多的重构帮助,将是对社区的有价值的补充。
+* 错误处理是社区的高优先级问题,它在冗长性和可调试性上带来了挑战。Go 团队目前没有公开提案,但正在继续探索改进错误处理的选项。
* 对受访者来说,入门和学习最佳实践是最大的挑战之一,将是未来研究的领域。
-* 对于Go模块维护者来说,保持依赖项更新、模块版本控制,以及识别或避免破坏性变化是最大的挑战。帮助维护者提供稳定和健康的生态系统是另一个感兴趣的进一步 UX 研究的话题。
+* 对于 Go 模块维护者来说,保持依赖项更新、模块版本控制,以及识别或避免破坏性变化是最大的挑战。帮助维护者提供稳定和健康的生态系统是另一个感兴趣的进一步 UX 研究的话题。
再次感谢所有参与并为这次调查做出贡献的人——没有你们我们无法完成这项工作。我们希望在今年晚些时候的下一次调查中再见到你。
\ No newline at end of file
diff --git a/2023/w20_How_to_troubleshoot_memory_leaks_in_Go_with_Grafana_Pyroscope.md b/2023/w20_How_to_troubleshoot_memory_leaks_in_Go_with_Grafana_Pyroscope.md
index 51d5033..3b70471 100644
--- a/2023/w20_How_to_troubleshoot_memory_leaks_in_Go_with_Grafana_Pyroscope.md
+++ b/2023/w20_How_to_troubleshoot_memory_leaks_in_Go_with_Grafana_Pyroscope.md
@@ -75,7 +75,7 @@ func longRunningTask(responses chan []byte) {
}
```
-上面的这段简单的代码,在连接到并行数据库的HTT服务器过程中,造成了内存泄露。因为 HTTP 请求不等待响应 channel,所以 longRunningTask 永远处于阻塞状态,从而泄露了用它创建的协程和资源。
+上面的这段简单的代码,在连接到并行数据库的 HTT 服务器过程中,造成了内存泄露。因为 HTTP 请求不等待响应 channel,所以 longRunningTask 永远处于阻塞状态,从而泄露了用它创建的协程和资源。
为了防止这种情况的发生,我们需要正确终止协程。我们可以使用各种技术来完成,比如使用 channel 来通知什么时候应该退出协程,或是使用 `context.Cancel` 将取消信号传播到协程,或是使用 `sync.WaitGroup` 确保所有协程在退出程序之前都已完成。
@@ -99,7 +99,7 @@ Go 中另一个常见的协程和资源泄漏的情况是没有正确释放 Time
(注:虽然这篇博文主要关注 Go 语言,但 Pyroscope 也支持其他语言的内存分析。)
-### 步骤1:确定内存泄漏的来源
+### 步骤 1:确定内存泄漏的来源
假设您已经进行了监控,第一步是使用日志、度量或跟踪找出系统哪个部分有问题
@@ -111,7 +111,7 @@ Go 中另一个常见的协程和资源泄漏的情况是没有正确释放 Time
一旦您确定了系统的部分和时间,您就可以使用持续分析来确定有问题的功能
-### 步骤2:将 Pyroscope 与您的应用程序集成
+### 步骤 2:将 Pyroscope 与您的应用程序集成
要开始分析 Go 应用程序,你需要在应用程序中包含我们的 Go 模块:
@@ -172,7 +172,7 @@ func main() {
要了解更多信息,请查看 [Pyroscope 文档](https://pyroscope.io/docs/golang/)。
-### 步骤3:下钻关联指标
+### 步骤 3:下钻关联指标
我建议你先看看 goroutines 随时间推移,是否有任何值得关注的问题,然后切换到内存调查。
@@ -217,7 +217,7 @@ func TestA(t *testing.T) {
-### 步骤5:修复内存泄漏
+### 步骤 5:修复内存泄漏
既然您可以重现并理解您的问题,那么是时候迭代修复并部署它进行测试了。您可以再次利用持续分析来监控您的变更并确认符合您的预期。
diff --git a/2023/w22_Build_your_Golang_package_docs_locally.md b/2023/w22_Build_your_Golang_package_docs_locally.md
index baae914..aaed1c4 100644
--- a/2023/w22_Build_your_Golang_package_docs_locally.md
+++ b/2023/w22_Build_your_Golang_package_docs_locally.md
@@ -26,7 +26,7 @@
[golang/pkgsite](https://github.com/golang/pkgsite) 托管了用于渲染文档的 `pkg.go.dev` 命令行界面。 `pkgsite` 命令的[注释](https://github.com/golang/pkgsite/blob/master/cmd/pkgsite/main.go#L5)虽然简洁,但可以让我们开始:
-```
+```plain
// Pkgsite 提取和生成 Go 程序的文档。
// 它作为 Web 服务器运行,将文档呈现为 Web 页面。
//
@@ -35,13 +35,13 @@
我们将使用它来设置一个本地服务器版本的 `pkg.go.dev`,其中包含 Golang 包的文档渲染功能。所以让我们安装它。我在其他地方看到的建议是本地克隆此存储库,构建它并将其安装在 `$PATH` 中的某个位置,但这个 `go install` 应该就可以工作:
-```
+```plain
$ go install golang.org/x/pkgsite/cmd/pkgsite@latest
```
一旦安装完成,只需将其运行在我们的 Go 包仓库所在的位置即可:
-```
+```plain
$ cd /path/to/go/pkg
$ pkgsite
2022/06/16 10:13:55 Info: Listening on addr http://localhost:8080
@@ -55,7 +55,7 @@ $ pkgsite
现在,类似于 pkg.go.dev,你可以通过将 `go.mod` 中的模块路径附加到 URL 上来检查本地模块的文档:
-```
+```plain
http://localhost:8080/my/local/module/path
```
@@ -75,7 +75,7 @@ http://localhost:8080/my/local/module/path
以下是我们将使用的关键命令。为每个进程打开一个新终端,或者只是将其中一个放在后台:
-```
+```plain
$ browser-sync start --proxy "localhost:8080"
[Browsersync] Proxying: http://localhost:8080
[Browsersync] Access URLs:
@@ -90,7 +90,7 @@ $ browser-sync start --proxy "localhost:8080"
我们需要设置这个代理的原因是,我们需要一种自动通知浏览器某些东西已经改变的方式。为此,`browser-sync` 在我们的请求中注入了一小段 JavaScript 代码,它会监听一个信号以确定何时重新加载。
-```
+```plain
$ nodemon --signal SIGTERM --watch my-mod.go --exec "browser-sync reload && pkgsite ."
[nodemon] 2.0.16
[nodemon] to restart at any time, enter `rs`
diff --git a/2023/w23_Go_Toolchains.md b/2023/w23_Go_Toolchains.md
index 6dd0ef6..459ec21 100644
--- a/2023/w23_Go_Toolchains.md
+++ b/2023/w23_Go_Toolchains.md
@@ -11,21 +11,21 @@
当前使用的 Go 工具链的选择取决于 `GOTOOLCHAIN` 环境设置以及主模块的 `go.mod` 文件或当前工作区的 `go.work` 文件中的 `go` 和 `toolchain` 行。当您在不同的主模块和工作区之间移动时,当前使用的工具链版本可能会有所不同,就像模块依赖版本一样。
-在标准配置中,`go` 命令在其捆绑的工具链与主模块或工作区中的 `go` 或 `toolchain` 行中的版本至少一样新时使用自己的捆绑工具链。例如,在使用 Go 1.21.3 捆绑的 `go` 命令中,主模块指定 `go 1.21.0` 时,`go` 命令使用 Go 1.21.3。当 `go` 或 `toolchain` 行比捆绑的工具链更新时,`go` 命令将运行更新的工具链。例如,在使用Go 1.21.3捆绑的go命令中,主模块声明 `go 1.21.9` 时,go 命令会查找并运行 `Go 1.21.9`。它首先在PATH中查找名为 `go1.21.9` 的程序,否则会下载并缓存 Go 1.21.9 工具链。可以禁用此自动工具链切换,但在这种情况下,为了更精确的向前兼容性,`go` 命令将拒绝在 `go` 行要求更高版本的 Go 的主模块或工作区中运行。也就是说,`go` 行设置了使用模块或工作区所需的最低 Go 版本。
+在标准配置中,`go` 命令在其捆绑的工具链与主模块或工作区中的 `go` 或 `toolchain` 行中的版本至少一样新时使用自己的捆绑工具链。例如,在使用 Go 1.21.3 捆绑的 `go` 命令中,主模块指定 `go 1.21.0` 时,`go` 命令使用 Go 1.21.3。当 `go` 或 `toolchain` 行比捆绑的工具链更新时,`go` 命令将运行更新的工具链。例如,在使用 Go 1.21.3 捆绑的 go 命令中,主模块声明 `go 1.21.9` 时,go 命令会查找并运行 `Go 1.21.9`。它首先在 PATH 中查找名为 `go1.21.9` 的程序,否则会下载并缓存 Go 1.21.9 工具链。可以禁用此自动工具链切换,但在这种情况下,为了更精确的向前兼容性,`go` 命令将拒绝在 `go` 行要求更高版本的 Go 的主模块或工作区中运行。也就是说,`go` 行设置了使用模块或工作区所需的最低 Go 版本。
-作为其他模块的依赖项的模块可能需要将最低Go版本要求设置为低于在该模块中直接工作时要使用的首选工具链。在这种情况下,`go.mod` 或 `go.work` 中的 `toolchain` 行设置了一个首选工具链,这个工具链在 `go` 命令决定使用哪个工具链时优先于 `go` 行。
+作为其他模块的依赖项的模块可能需要将最低 Go 版本要求设置为低于在该模块中直接工作时要使用的首选工具链。在这种情况下,`go.mod` 或 `go.work` 中的 `toolchain` 行设置了一个首选工具链,这个工具链在 `go` 命令决定使用哪个工具链时优先于 `go` 行。
可以将 `go` 和 `toolchain` 行视为指定模块对 Go 工具链本身的依赖关系的版本要求,就像 `go.mod` 中的 `require` 行指定对其他模块的依赖关系的版本要求一样。`go get` 命令管理 Go 工具链依赖关系,就像管理对其他模块的依赖关系一样。例如,`go get go@latest` 会更新模块以要求最新发布的 Go 工具链。
`GOTOOLCHAIN` 环境设置可以强制使用特定的 Go 版本,覆盖 `go` 和 `toolchain` 行。例如,要使用 Go 1.21rc3 测试一个包:
-```
+```plain
GOTOOLCHAIN=go1.21rc3 go test
```
-默认的 `GOTOOLCHAIN` 设置为 `auto`,这启用了之前描述的工具链切换。替代形式 `+auto` 在决定是否进一步切换之前设置要使用的默认工具链。例如,`GOTOOLCHAIN=go1.21.3+auto` 指示 `go` 命令从默认使用 Go 1.21.3 开始做出决策,但如果由 `go` 和 `toolchain` 行指示,则仍然使用更新的工具链。由于默认的 `GOTOOLCHAIN` 设置可以通过 `go env -w` 更改,因此如果您安装了Go 1.21.0或更高版本,那么:
+默认的 `GOTOOLCHAIN` 设置为 `auto`,这启用了之前描述的工具链切换。替代形式 `+auto` 在决定是否进一步切换之前设置要使用的默认工具链。例如,`GOTOOLCHAIN=go1.21.3+auto` 指示 `go` 命令从默认使用 Go 1.21.3 开始做出决策,但如果由 `go` 和 `toolchain` 行指示,则仍然使用更新的工具链。由于默认的 `GOTOOLCHAIN` 设置可以通过 `go env -w` 更改,因此如果您安装了 Go 1.21.0 或更高版本,那么:
-```
+```plain
go env -w GOTOOLCHAIN=go1.21.3+auto
```
@@ -37,13 +37,13 @@ go env -w GOTOOLCHAIN=go1.21.3+auto
发布的 Go 版本使用版本语法 “1.N.P”,表示 Go 1.N 的第 _P_ 次已发布版本。初始版本为 1.N.0,例如 “1.21.0”。后续版本如 1.N.9 通常被称为补丁版本。
-Go 1._N_的发布候选版本,在 1.N.0 之前发布,使用 ‘1.N_rc_R’ 的版本语法。Go 1.N 的第一个发布候选版本是1._N_rc1,如 `1.23rc1`。
+Go 1._N_的发布候选版本,在 1.N.0 之前发布,使用 ‘1.N_rc_R’ 的版本语法。Go 1.N 的第一个发布候选版本是 1._N_rc1,如 `1.23rc1`。
语法 “1.N” 被称为“语言版本”。它表示实现该版本 Go 语言和标准库的 Go 发布的整个系列。
-Go 版本的语言版本是将 N 之后的所有内容截断的结果:1.21、1.21rc2 和 1.21.3都实现了语言版本 1.21。
+Go 版本的语言版本是将 N 之后的所有内容截断的结果:1.21、1.21rc2 和 1.21.3 都实现了语言版本 1.21。
-已发布的 Go 工具链如 Go 1.21.0 和 Go 1.21rc1 报告特定版本(例如 `go1.21.0` 或 `go1.21rc1`)从 `go version` 和`[runtime.Version](/pkg/runtime/#Version)`中。未发布(仍在开发中)的Go工具链从Go开发存储库构建,只报告语言版本(例如 `go1.21`)。
+已发布的 Go 工具链如 Go 1.21.0 和 Go 1.21rc1 报告特定版本(例如 `go1.21.0` 或 `go1.21rc1`)从 `go version` 和`[runtime.Version](/pkg/runtime/#Version)`中。未发布(仍在开发中)的 Go 工具链从 Go 开发存储库构建,只报告语言版本(例如 `go1.21`)。
任何两个 Go 版本都可以进行比较,以确定一个是否小于、大于或等于另一个。如果语言版本不同,那么就决定了比较:1.21.9 < 1.22。在语言版本内,从最小到最大的排序是:语言版本本身,然后按_R_排序的发布候选版本,然后按_P_排序的发布版本。
@@ -71,7 +71,7 @@ Go 模块和工作区在其 `go. mod` 或 `go.work` 文件中指定与版本相
`go` 行声明了使用模块或工作区所需的最低 Go 版本。出于兼容性原因,如果 `go. mod` 文件中省略了 `go` 行,则该模块被认为具有隐式 `go 1.16` 行,如果 `go.work` 文件中省略了 go 行,则该工作区被认为具有隐式 `go 1.18` 行。
-`toolchain` 行声明了与模块或工作区一起使用的建议工具链。如下面的 “[Go工具链选择](https://go.dev/doc/toolchain#select])”中所述,如果默认工具链的版本小于建议工具链的版本,则 `go` 命令可以在该模块或工作区中运行此特定工具链。如果省略 `toolchain` 行,则模块或工作区被认为具有隐式 `toolchain go_V_` 行,其中 _V_ 是 `go` 行的 Go 版本。
+`toolchain` 行声明了与模块或工作区一起使用的建议工具链。如下面的 “[Go 工具链选择](https://go.dev/doc/toolchain#select])”中所述,如果默认工具链的版本小于建议工具链的版本,则 `go` 命令可以在该模块或工作区中运行此特定工具链。如果省略 `toolchain` 行,则模块或工作区被认为具有隐式 `toolchain go_V_` 行,其中 _V_ 是 `go` 行的 Go 版本。
例如,一个说 `go 1.21.0` 但没有 `toolchain` 行的 `go. mod` 被解释为它有一个 `toolchain go1.21.0` 行。
@@ -99,7 +99,7 @@ Go 工具链拒绝加载声明最低所需 Go 版本大于工具链自己版本
- 否则,如果在用户的环境默认文件(使用 [`go env -w` 和 `go env -u`](https://go.dev/cmd/go/#hdr-Print_Go_environment_information) 管理)中设置了 `GOTOOLCHAIN`,则 `go` 命令使用该值。
-- 否则,如果在捆绑的 Go 工具链的环境默认文件(`$GOROOT/go. env)中设置了 `GOTOOLCHAIN`,则 `go` 命令使用该值。
+- 否则,如果在捆绑的 Go 工具链的环境默认文件(`$GOROOT/go. env)中设置了`GOTOOLCHAIN`,则`go` 命令使用该值。
在标准 Go 工具链中,`$GOROOT/go.env` 文件设置默认的 `GOTOOLCHAIN=auto`,但重新打包的 Go 工具链可能会更改此值。
@@ -134,7 +134,7 @@ Go 工具链拒绝加载声明最低所需 Go 版本大于工具链自己版本
每当 `go` 命令在启动工具链选择后切换工具链时,它都会打印一条解释原因的消息。例如:
-```
+```plain
go: module example.com/widget@v1.2.3 requires go >= 1.24rc1; switching to go 1.27.9
```
@@ -158,7 +158,7 @@ go: module example.com/widget@v1.2.3 requires go >= 1.24rc1; switching to go 1.2
## 下载 toolchains
-当使用 `GOTOOLCHAIN=auto` 或 `GOTOOLCHAIN=+auto` 时,Go 命令会根据需要下载更新的工具链。这些工具链被打包为特殊模块,模块路径为 `golang.org/toolchain`,版本为 `v0.0.1-goVERSION.GOOS-GOARCH`。工具链像其他模块一样被下载,这意味着可以通过设置 `GOPROXY` 来代理工具链下载,并通过Go校验和数据库检查它们的校验和。由于使用的特定工具链取决于系统自己的默认工具链以及本地操作系统和架构(GOOS 和 GOARCH),因此在 `go.sum` 中编写工具链模块的校验和不实际。相反,如果 `GOSUMDB=off`,则工具链下载失败缺乏验证。`GOPRIVATE` 和 `GONOSUMDB` 模式不适用于工具链下载。
+当使用 `GOTOOLCHAIN=auto` 或 `GOTOOLCHAIN=+auto` 时,Go 命令会根据需要下载更新的工具链。这些工具链被打包为特殊模块,模块路径为 `golang.org/toolchain`,版本为 `v0.0.1-goVERSION.GOOS-GOARCH`。工具链像其他模块一样被下载,这意味着可以通过设置 `GOPROXY` 来代理工具链下载,并通过 Go 校验和数据库检查它们的校验和。由于使用的特定工具链取决于系统自己的默认工具链以及本地操作系统和架构(GOOS 和 GOARCH),因此在 `go.sum` 中编写工具链模块的校验和不实际。相反,如果 `GOSUMDB=off`,则工具链下载失败缺乏验证。`GOPRIVATE` 和 `GONOSUMDB` 模式不适用于工具链下载。
## 使用 `go get` 管理 Go 版本模块的需求
diff --git a/2023/w24_Top_3_Design_Patterns_for_a_Large_Go_Codebase.md b/2023/w24_Top_3_Design_Patterns_for_a_Large_Go_Codebase.md
index f0f885e..17fc8fa 100644
--- a/2023/w24_Top_3_Design_Patterns_for_a_Large_Go_Codebase.md
+++ b/2023/w24_Top_3_Design_Patterns_for_a_Large_Go_Codebase.md
@@ -18,7 +18,7 @@
## 设计模式的简史
-让我们回顾一下软件工程设计模式是如何产生的。 1994 年,四位作者——Erich Gamma、John Vlissides、Ralph Johnson 和 Richard Helm 出版了一本书“[设计模式:可复用面向对象软件的基础](https://www.oreilly.com/library/view/design-patterns-elements/0201633612/)”。 这本书赢得了“四人帮之书”的绰号,后来缩写为“GoF之书”。
+让我们回顾一下软件工程设计模式是如何产生的。 1994 年,四位作者——Erich Gamma、John Vlissides、Ralph Johnson 和 Richard Helm 出版了一本书“[设计模式:可复用面向对象软件的基础](https://www.oreilly.com/library/view/design-patterns-elements/0201633612/)”。 这本书赢得了“四人帮之书”的绰号,后来缩写为“GoF 之书”。
GoF 书中包含 23 种设计模式,分为三种类型:创建型设计模式、结构型设计模式和行为型设计模式。 本书的示例是用 C++ 和 Smalltalk 编写的。 不出意外的,这本书所描述的模式受到这些特定语言和当时软件工程需求的影响。 然而,设计模式的概念被证明是持久的。
diff --git a/2023/w25_Using_Bitmaps_to_Perform_Range_Queries.md b/2023/w25_Using_Bitmaps_to_Perform_Range_Queries.md
index a4c76da..c49390f 100644
--- a/2023/w25_Using_Bitmaps_to_Perform_Range_Queries.md
+++ b/2023/w25_Using_Bitmaps_to_Perform_Range_Queries.md
@@ -43,7 +43,7 @@ _两个特征的逐位交集_
_Captivity 计数表示为单个位图_
-这种方法是可以的,但它有几个局限性。首先,它的效率不是很高。根据基数的不同,您可能需要创建大量位图来表示所有可能的值。其次,如果您想按 Captivity 值的范围筛选查询,则必须对范围内的每个可能值执行 OR 运算。为了知道样本里哪些动物的 Captivity 值少于100个,您的查询需要执行以下操作:(Captivity=99或Captivity=98或Captivity=97或…)。你明白了。
+这种方法是可以的,但它有几个局限性。首先,它的效率不是很高。根据基数的不同,您可能需要创建大量位图来表示所有可能的值。其次,如果您想按 Captivity 值的范围筛选查询,则必须对范围内的每个可能值执行 OR 运算。为了知道样本里哪些动物的 Captivity 值少于 100 个,您的查询需要执行以下操作:(Captivity=99 或 Captivity=98 或 Captivity=97 或……)。你明白了。
另一种方法是创建 Captivity 范围的存储桶,而不是将每个可能的值表示为唯一的位图。在这种情况下,您可能会遇到这样的情况:
@@ -63,7 +63,7 @@ _Captivity 计数(以桶表示)_
_Captivity 计数用范围编码位图_
-用范围编码位图表示一个值与我们使用相等编码所做的类似,但我们不只是设置与特定值对应的位,而是为每个大于实际值的值设置一个位。例如,因为有14只考拉熊的圈养(captivity),我们在位图 14 以及位图 15、16、17 等中设置了 bit。位图现在表示的不是具有特定圈养数量的所有动物的位图,而是具有并包括该数量的圈养数量的全部动物的位图。
+用范围编码位图表示一个值与我们使用相等编码所做的类似,但我们不只是设置与特定值对应的位,而是为每个大于实际值的值设置一个位。例如,因为有 14 只考拉熊的圈养(captivity),我们在位图 14 以及位图 15、16、17 等中设置了 bit。位图现在表示的不是具有特定圈养数量的所有动物的位图,而是具有并包括该数量的圈养数量的全部动物的位图。
这种编码方法使我们能够执行以前所做的范围查询,但我们可以从一个或两个位图中获得所需的内容,而不是对许多不同的位图执行 OR 运算。
例如,如果我们想知道哪些动物圈养的标本少于 15 个,我们只需提取 14 位图就可以了。如果我们想知道哪些动物的圈养标本超过 15 个,这有点复杂,但并不多。为此,我们提取表示最大计数的位图(在我们的情况下是 956 位图),然后减去 15 位图。
@@ -92,7 +92,7 @@ _十进制,位切片索引_
_范围编码,十进制,位切片索引_
-请注意,每个组件中最重要的值(在基数为10的情况下为9)始终为 1。正因为如此,我们不需要存储最高值。因此,对于十进制的范围编码的位切片索引,我们只需要9个位图来表示一个组件。除此之外,我们还需要存储一个名为“Not Null”的位图,它指示是否为该列设置了值。下一个图表显示了生成的位图。
+请注意,每个组件中最重要的值(在基数为 10 的情况下为 9)始终为 1。正因为如此,我们不需要存储最高值。因此,对于十进制的范围编码的位切片索引,我们只需要 9 个位图来表示一个组件。除此之外,我们还需要存储一个名为“Not Null”的位图,它指示是否为该列设置了值。下一个图表显示了生成的位图。
![](https://uploads-ssl.webflow.com/62fe6fb36d2e0b5c203ee7c3/6324dd62797efe87f0dfff68_captive-bsi-range-encoded-base10-not-null.png)
@@ -108,7 +108,7 @@ _范围编码,十进制,位切片索引,不为空_
_范围编码,二进制,位切片索引_
-第一列比特表示以 2 为基数的值 000000011,这是圈养海牛的数量(以 10 为基数为 3)。由于 000000011 的组件 0 和组件 1 都是 1,我们在组件 0 的行 1 和组件 1 的行 1 中设置一个位。由于 000000011的 其余组件都是 0,我们在组件 2 到 9 的第 0 行设置了一个比特,也因为这些组件是范围编码的,我们在每个大于 0 的值中设置一个比特。在二进制的组件的情况下,这意味着我们还为组件 2 到 9设置了行 1 中的位。
+第一列比特表示以 2 为基数的值 000000011,这是圈养海牛的数量(以 10 为基数为 3)。由于 000000011 的组件 0 和组件 1 都是 1,我们在组件 0 的行 1 和组件 1 的行 1 中设置一个位。由于 000000011 的 其余组件都是 0,我们在组件 2 到 9 的第 0 行设置了一个比特,也因为这些组件是范围编码的,我们在每个大于 0 的值中设置一个比特。在二进制的组件的情况下,这意味着我们还为组件 2 到 9 设置了行 1 中的位。
但请记住,就像我们之前在十进制表示的位图 9 中看到的那样,位图 1 总是一个,所以我们不需要存储它。这就给我们留下了这样的方式:
@@ -116,7 +116,7 @@ _范围编码,二进制,位切片索引_
_范围编码,二进制,不为空的位切片索引_
-通过这种编码,我们可以仅用 10 个位图来表示样本值的范围!此外,请注意,二进制、范围编码、位切片索引是整数值的二进制表示的反。这告诉我们,我们可以仅使用(n+1)位图(其中附加位图是“Not Null”位图)来表示基数为n的任何范围的值。这意味着我们可以对大整数值执行范围查询,而不需要存储不合理数量的位图。
+通过这种编码,我们可以仅用 10 个位图来表示样本值的范围!此外,请注意,二进制、范围编码、位切片索引是整数值的二进制表示的反。这告诉我们,我们可以仅使用(n+1)位图(其中附加位图是“Not Null”位图)来表示基数为 n 的任何范围的值。这意味着我们可以对大整数值执行范围查询,而不需要存储不合理数量的位图。
## FeatureBase 中的范围编码位图
diff --git a/2023/w27_Understanding_Allocations_in_Go.md b/2023/w27_Understanding_Allocations_in_Go.md
index 38545f4..adf459c 100644
--- a/2023/w27_Understanding_Allocations_in_Go.md
+++ b/2023/w27_Understanding_Allocations_in_Go.md
@@ -4,7 +4,7 @@
- 原文作者:James Kirk
- 本文永久链接:https://github.com/gocn/translator/blob/master/2021/
- 译者:[lsj1342](https://github.com/lsj1342)
-- 校对:[]()
+- 校对:[](https://example.com)
***
## Introduction
@@ -13,7 +13,7 @@ Thanks to efficient in-built memory management in the Go runtime, we’re genera
Anyone who’s run a benchmark with the `-benchmem` flag will have seen the `allocs/op` stat in output like the below. In this post we’ll look at what counts as an alloc and what we can do to influence this number.
-```
+```plain
BenchmarkFunc-8 67836464 16.0 ns/op 8 B/op 1 allocs/op
```
@@ -65,7 +65,7 @@ Since compiler implementations change over time, **there’s no way of knowing w
For escape analysis results, the `-m` option (`print optimization decisions`) can be used. Let’s test this with a simple program that creates two stack frames for functions `main1` and `stackIt`.
-```
+```plain
func main1() {
_ = stackIt()
}
@@ -78,21 +78,21 @@ func stackIt() int {
Since we can can’t discuss stack behaviour if the compiler removes our function calls, the `noinline` [pragma](https://dave.cheney.net/2018/01/08/gos-hidden-pragmas) is used to prevent inlining when compiling the code. Let’s take a look at what the compiler has to say about its optimization decisions. The `-l` option is used to omit inlining decisions.
-```
+```plain
$ go build -gcflags '-m -l'
# github.com/Jimeux/go-samples/allocations
```
Here we see that no decisions were made regarding escape analysis. In other words, variable `y` remained on the stack, and didn’t trigger any heap allocations. We can verify this with a benchmark.
-```
+```plain
$ go test -bench . -benchmem
BenchmarkStackIt-8 680439016 1.52 ns/op 0 B/op 0 allocs/op
```
As expected, the `allocs/op` stat is `0`. An important observation we can make from this result is that **copying variables can allow us to keep them on the stack** and avoid allocation to the heap. Let’s verify this by modifying the program to avoid copying with use of a pointer.
-```
+```plain
func main2() {
_ = stackIt2()
}
@@ -106,7 +106,7 @@ func stackIt2() *int {
Let’s see the compiler output.
-```
+```plain
go build -gcflags '-m -l'
# github.com/Jimeux/go-samples/allocations
./main.go:10:2: moved to heap: res
@@ -114,14 +114,14 @@ go build -gcflags '-m -l'
The compiler tells us it moved the pointer `res` to the heap, which triggers a heap allocation as verified in the benchmark below
-```
+```plain
$ go test -bench . -benchmem
BenchmarkStackIt2-8 70922517 16.0 ns/op 8 B/op 1 allocs/op
```
So does this mean pointers are guaranteed to create allocations? Let’s modify the program again to this time pass a pointer down the stack.
-```
+```plain
func main3() {
y := 2
_ = stackIt3(&y) // pass y down the stack as a pointer
@@ -136,14 +136,14 @@ func stackIt3(y *int) int {
Yet running the benchmark shows nothing was allocated to the heap.
-```
+```plain
$ go test -bench . -benchmem
BenchmarkStackIt3-8 705347884 1.62 ns/op 0 B/op 0 allocs/op
```
The compiler output tells us this explicitly.
-```
+```plain
$ go build -gcflags '-m -l'
# github.com/Jimeux/go-samples/allocations
./main.go:10:14: y does not escape
@@ -159,7 +159,7 @@ Why do we get this seeming inconsistency? `stackIt2` passes `res` _up the stack_
We’ve learnt a little about what the `alloc` in `allocs/op` means, and how to verify if an allocation to the heap is triggered, but why should we care if this stat is non-zero in the first place? The benchmarks we’ve already done can begin to answer this question.
-```
+```plain
BenchmarkStackIt-8 680439016 1.52 ns/op 0 B/op 0 allocs/op
BenchmarkStackIt2-8 70922517 16.0 ns/op 8 B/op 1 allocs/op
BenchmarkStackIt3-8 705347884 1.62 ns/op 0 B/op 0 allocs/op
@@ -183,7 +183,7 @@ Many aspects of performance don’t become apparent without production condition
We’re not going to recreate an entire application in this post, but we will take a look at some more detailed performance diagnostics using the [trace tool](https://golang.org/cmd/trace/). Let’s begin by defining a (somewhat) big struct with nine fields.
-```
+```plain
type BigStruct struct {
A, B, C int
D, E, F string
@@ -193,7 +193,7 @@ type BigStruct struct {
Now we’ll define two functions: `CreateCopy`, which copies `BigStruct` instances between stack frames, and `CreatePointer`, which shares `BigStruct` pointers up the stack, avoiding copying, but resulting in heap allocations.
-```
+```plain
//go:noinline
func CreateCopy() BigStruct {
return BigStruct{
@@ -214,7 +214,7 @@ func CreatePointer() *BigStruct {
We can verify the explanation from above with the techniques used so far.
-```
+```plain
$ go build -gcflags '-m -l'
./main.go:67:9: &BigStruct literal escapes to heap
$ go test -bench . -benchmem
@@ -224,7 +224,7 @@ BenchmarkPointerIt-8 20393278 52.6 ns/op 80 B/op 1 allocs/op
Here are the tests we’ll use for the `trace` tool. They each create 20,000,000 instances of `BigStruct` with their respective `Create` function.
-```
+```plain
const creations = 20_000_000
func TestCopyIt(t *testing.T) {
@@ -242,7 +242,7 @@ func TestPointerIt(t *testing.T) {
Next we’ll save the trace output for `CreateCopy` to file `copy_trace.out`, and open it with the trace tool in the browser.
-```
+```plain
$ go test -run TestCopyIt -trace=copy_trace.out
PASS
ok github.com/Jimeux/go-samples/allocations 0.281s
@@ -260,7 +260,7 @@ Trace for 20,000,000 CreateCopy calls
Let’s generate the trace data for the `CreatePointer` code.
-```
+```plain
$ go test -run TestPointerIt -trace=pointer_trace.out
PASS
ok github.com/Jimeux/go-samples/allocations 2.224s
@@ -296,7 +296,7 @@ Top-level goroutine analysis for CreatePointer
A look at some of the stats available elsewhere in the trace data further illustrates the cost of heap allocation, with a stark difference in the number of goroutines generated, and almost 400 STW (stop the world) events for the `CreatePointer` test.
-```
+```plain
+------------+------+---------+
| | Copy | Pointer |
+------------+------+---------+