Skip to content

Commit 7aa4f38

Browse files
committed
docs(新增): 增加文档
1 parent a44cead commit 7aa4f38

6 files changed

+615
-68
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
文章目录:
2+
[TOC]
3+
4+
## 不要使用 + 和 fmt.Sprintf 操作字符串
5+
6+
不要使用 `+``fmt.Sprintf` 操作字符串,虽然很方便,但是真的很慢!
7+
8+
我们要使用 `bytes.NewBufferString` 进行处理。
9+
10+
基准测试如下:
11+
12+
### +
13+
14+
```
15+
func BenchmarkStringOperation1(b *testing.B) {
16+
b.ResetTimer()
17+
str := ""
18+
for i := 0; i < b.N; i++ {
19+
str += "golang"
20+
}
21+
}
22+
23+
// 输出
24+
goos: darwin
25+
goarch: amd64
26+
pkg: demo/stringoperation
27+
cpu: Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
28+
BenchmarkStringOperation1
29+
BenchmarkStringOperation1-12 353318 114135 ns/op
30+
PASS
31+
32+
Process finished with the exit code 0
33+
```
34+
35+
### fmt.Sprintf
36+
37+
```
38+
func BenchmarkStringOperation2(b *testing.B) {
39+
b.ResetTimer()
40+
str := ""
41+
for i := 0; i < b.N; i++ {
42+
str = fmt.Sprintf("%s%s", str, "golang")
43+
}
44+
}
45+
46+
// 输出
47+
goos: darwin
48+
goarch: amd64
49+
pkg: demo/stringoperation
50+
cpu: Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
51+
BenchmarkStringOperation2
52+
BenchmarkStringOperation2-12 280140 214098 ns/op
53+
PASS
54+
55+
Process finished with the exit code 0
56+
```
57+
58+
### bytes.NewBufferString
59+
60+
```
61+
func BenchmarkStringOperation3(b *testing.B) {
62+
b.ResetTimer()
63+
strBuf := bytes.NewBufferString("")
64+
for i := 0; i < b.N; i++ {
65+
strBuf.WriteString("golang")
66+
}
67+
}
68+
69+
// 输出
70+
goos: darwin
71+
goarch: amd64
72+
pkg: demo/stringoperation
73+
cpu: Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
74+
BenchmarkStringOperation3
75+
BenchmarkStringOperation3-12 161292136 8.582 ns/op
76+
PASS
77+
78+
Process finished with the exit code 0
79+
```
80+
## 对于固定字段的键值对,不要使用 map[string]interface{}
81+
82+
对于固定字段的键值对,不要使用 `map[string]interface{}`!
83+
84+
我们要使用`临时 Struct`
85+
86+
基准测试如下:
87+
88+
### map[string]interface{}
89+
90+
```
91+
func BenchmarkStructOperation1(b *testing.B) {
92+
b.ResetTimer()
93+
for i := 0; i < b.N; i++ {
94+
var demo = map[string]interface{}{}
95+
demo["Name"] = "Tom"
96+
demo["Age"] = 30
97+
}
98+
}
99+
100+
// 输出
101+
goos: darwin
102+
goarch: amd64
103+
pkg: demo/structoperation
104+
cpu: Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
105+
BenchmarkStructOperation1
106+
BenchmarkStructOperation1-12 43300134 27.97 ns/op
107+
PASS
108+
109+
Process finished with the exit code 0
110+
```
111+
112+
### 临时 Struct
113+
114+
```
115+
func BenchmarkStructOperation2(b *testing.B) {
116+
b.ResetTimer()
117+
for i := 0; i < b.N; i++ {
118+
var demo struct {
119+
Name string
120+
Age int
121+
}
122+
demo.Name = "Tom"
123+
demo.Age = 30
124+
}
125+
}
126+
127+
// 输出
128+
oos: darwin
129+
goarch: amd64
130+
pkg: demo/structoperation
131+
cpu: Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz
132+
BenchmarkStructOperation2
133+
BenchmarkStructOperation2-12 1000000000 0.2388 ns/op
134+
PASS
135+
136+
Process finished with the exit code 0
137+
```
138+
139+
## 小结
140+
141+
你有类似这样的注意点吗,欢迎留言~
142+
143+
下面推荐阅读的这几篇文章也是关于开发中需要知道的小技术点,更多技术细节和代码讨论,可以加入到我的星球。
144+
145+
### 推荐阅读
146+
147+
- [函数的不定参数你是这样用吗?](https://mp.weixin.qq.com/s/jvSbZ0_g_EFqaR2TmjjO8w)
148+
- [优雅地处理错误真是一门学问啊!](https://mp.weixin.qq.com/s/W_LsZtnjGIKQ-LB6EkRgBA)
149+
- [如何设计 API 接口,实现统一格式返回?](https://mp.weixin.qq.com/s/6c6uapjIzJC9wmjUFyZuZA)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
**文章目录:**
2+
[TOC]
3+
4+
## 前言
5+
6+
`sync.Pool` 是临时对象池,存储的是临时对象,不可以用它来存储 `socket` 长连接和数据库连接池等。
7+
8+
`sync.Pool` 本质是用来保存和复用临时对象,以减少内存分配,降低 GC 压力,比如需要使用一个对象,就去 Pool 里面拿,如果拿不到就分配一份,这比起不停生成新的对象,用完了再等待 GC 回收要高效的多。
9+
10+
## sync.Pool
11+
12+
`sync.Pool` 的使用很简单,看下示例代码:
13+
14+
```
15+
package student
16+
17+
import (
18+
"sync"
19+
)
20+
21+
type student struct {
22+
Name string
23+
Age int
24+
}
25+
26+
var studentPool = &sync.Pool{
27+
New: func() interface{} {
28+
return new(student)
29+
},
30+
}
31+
32+
func New(name string, age int) *student {
33+
stu := studentPool.Get().(*student)
34+
stu.Name = name
35+
stu.Age = age
36+
return stu
37+
}
38+
39+
func Release(stu *student) {
40+
stu.Name = ""
41+
stu.Age = 0
42+
studentPool.Put(stu)
43+
}
44+
```
45+
46+
当使用 `student` 对象时,只需要调用 `New()` 方法获取对象,获取之后使用 `defer` 函数进行释放即可。
47+
48+
```
49+
stu := student.New("tom", 30)
50+
defer student.Release(stu)
51+
52+
// 业务逻辑
53+
...
54+
55+
```
56+
57+
关于 `sync.Pool` 里面的对象具体是什么时候真正释放,是由系统决定的。
58+
59+
## 小结
60+
61+
1. 一定要注意存储的是临时对象!
62+
2. 一定要注意 `Get` 后,要调用 `Put`
63+
64+
以上,希望对你能够有所帮助。
65+
66+
## 推荐阅读
67+
68+
- [Go - 使用 options 设计模式](https://mp.weixin.qq.com/s/jvSbZ0_g_EFqaR2TmjjO8w)
69+
- [Go - json.Unmarshal 遇到的小坑](https://mp.weixin.qq.com/s/ykZCZb9IAXJaKAx_cO7YjA)
70+
- [Go - 两个在开发中需注意的小点](https://mp.weixin.qq.com/s/-QCG61vh6NVJUWz6tOY7Gw)
71+
- [Go - time.RFC3339 时间格式化](https://mp.weixin.qq.com/s/1pFVaMaWItp8zCXotQ9iBg)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
**文章目录:**
2+
[TOC]
3+
4+
## 前言
5+
6+
为什么需要了解逃逸分析?
7+
8+
因为我们想要提升程序性能,通过逃逸分析我们能够知道变量是分配到堆上还是栈上,如果分配到栈上,内存的分配和释放都是由编译器进行管理,分配和释放的速度非常快,如果分配到堆上,堆不像栈那样可以自动清理,它会引起频繁地进行垃圾回收(`GC`),而垃圾回收会占用比较大的系统开销。
9+
10+
## 什么是逃逸分析?
11+
12+
> 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法,简单来说就是分析在程序的哪些地方可以访问到该指针。
13+
14+
简单的说,它是在对变量放到堆上还是栈上进行分析,该分析在编译阶段完成。如果一个变量超过了函数调用的生命周期,也就是这个变量在函数外部存在引用,编译器会把这个变量分配到堆上,这时我们就说这个变量发生逃逸了。
15+
16+
## 如何确定是否逃逸?
17+
18+
```
19+
go run -gcflags '-m -l' main.go
20+
```
21+
22+
## 可能出现逃逸的场景
23+
24+
### 01
25+
26+
```
27+
package main
28+
29+
type Student struct {
30+
Name interface{}
31+
}
32+
33+
func main() {
34+
stu := new(Student)
35+
stu.Name = "tom"
36+
37+
}
38+
```
39+
40+
分析结果:
41+
42+
```
43+
go run -gcflags '-m -l' 01.go
44+
# command-line-arguments
45+
./01.go:8:12: new(Student) does not escape
46+
./01.go:9:11: "tom" escapes to heap
47+
```
48+
49+
`interface{}` 赋值,会发生逃逸,优化方案是将类型设置为固定类型,例如:`string`
50+
51+
```
52+
package main
53+
54+
type Student struct {
55+
Name string
56+
}
57+
58+
func main() {
59+
stu := new(Student)
60+
stu.Name = "tom"
61+
62+
}
63+
```
64+
65+
分析结果:
66+
67+
```
68+
go run -gcflags '-m -l' 01.go
69+
# command-line-arguments
70+
./01.go:8:12: new(Student) does not escape
71+
```
72+
73+
### 02
74+
75+
```
76+
package main
77+
78+
type Student struct {
79+
Name string
80+
}
81+
82+
func GetStudent() *Student {
83+
stu := new(Student)
84+
stu.Name = "tom"
85+
return stu
86+
}
87+
88+
func main() {
89+
GetStudent()
90+
}
91+
92+
```
93+
94+
分析结果:
95+
96+
```
97+
go run -gcflags '-m -l' 02.go
98+
# command-line-arguments
99+
./02.go:8:12: new(Student) escapes to heap
100+
```
101+
102+
返回指针类型,会发生逃逸,优化方案视情况而定。
103+
104+
函数传递指针和传值哪个效率高吗?我们知道传递指针可以减少底层值的拷贝,可以提高效率,但是如果拷贝的数据量小,由于指针传递会产生逃逸,可能会使用堆,也可能会增加 `GC` 的负担,所以传递指针不一定是高效的。
105+
106+
不要盲目使用变量指针作为参数,虽然减少了复制,但变量逃逸的开销可能更大。
107+
108+
### 03
109+
110+
```
111+
package main
112+
113+
func main() {
114+
nums := make([]int, 10000, 10000)
115+
116+
for i := range nums {
117+
nums[i] = i
118+
}
119+
}
120+
```
121+
122+
分析结果:
123+
124+
```
125+
go run -gcflags '-m -l' 03.go
126+
# command-line-arguments
127+
./03.go:4:14: make([]int, 10000, 10000) escapes to heap
128+
```
129+
130+
栈空间不足,会发生逃逸,优化方案尽量设置容量,如果容量实在过大那就没办法了。
131+
132+
## 小结
133+
134+
1. 逃逸分析是编译器在静态编译时完成的。
135+
2. 逃逸分析后可以确定哪些变量可以分配在栈上,栈的性能好。
136+
137+
以上,希望对你能够有所帮助。
138+
139+
## 推荐阅读
140+
141+
- [Go - 使用 sync.Pool 来减少 GC 压力](https://mp.weixin.qq.com/s/0NVp59uI8h9WTp68wtb7XQ)
142+
- [Go - 使用 options 设计模式](https://mp.weixin.qq.com/s/jvSbZ0_g_EFqaR2TmjjO8w)
143+
- [Go - json.Unmarshal 遇到的小坑](https://mp.weixin.qq.com/s/ykZCZb9IAXJaKAx_cO7YjA)
144+
- [Go - 两个在开发中需注意的小点](https://mp.weixin.qq.com/s/-QCG61vh6NVJUWz6tOY7Gw)
145+
- [Go - time.RFC3339 时间格式化](https://mp.weixin.qq.com/s/1pFVaMaWItp8zCXotQ9iBg)

0 commit comments

Comments
 (0)