其他
# Go的基本命令
// 用于打包编译代码
go build
// 来移除当前源码包和关联源码包里面编译生成的文件
go clean
// 格式化代码文件
go fmt
// 下载安装包
go get
// 这里就是先生成结果文件,然后把这个文件放到我们的包里面去
go install
// 测试文件自动读取源码目录下面名为*_test.go的文件,生成并运行测试用的可执行文件
go test
// go提供的一些工具
go tool fix //用来修复以前老版本的代码到新版本
go tool vet directory|files //用来分析当前目录的代码是否都是正确的代码
// 用于在编译前自动化生成某类代码。go generate和go build是完全不一样的命令,通过分析源码中特殊的注释,然后执行相应的命令。这些命令都是很明确的,没有任何的依赖在里面。而且大家在用这个之前心里面一定要有一个理念,这个go generate是给你用的,不是给使用你这个包的人用的,是方便你来生成一些代码的。
go generate
// 生成文档
go doc
go version 查看go当前的版本
go env 查看当前go的环境变量
go list 列出当前全部安装的package
go run 编译并运行Go程序
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
参考 Go 基本命令_kf_panda-CSDN博客 (opens new window)
# 利用CPU缓存优化Go程序
常见问题就是下面那段代码执行速度更快
func createMatrix(size int) [][]int64 {
matrix := make([][]int64, size)
for i := 0; i < size; i++ {
matrix[i] = make([]int64, size)
}
return matrix
}
const matrixLength = 6400
func BenchmarkMatrixCombination(b *testing.B) {
matrixA := createMatrix(matrixLength)
matrixB := createMatrix(matrixLength)
for n := 0; n < b.N; n++ {
for i := 0; i < matrixLength; i++ {
for j := 0; j < matrixLength; j++ {
matrixA[i][j] = matrixA[i][j] + matrixB[i][j]
}
}
}
}
func BenchmarkMatrixReversedCombination(b *testing.B) {
matrixA := createMatrix(matrixLength)
matrixB := createMatrix(matrixLength)
for n := 0; n < b.N; n++ {
for i := 0; i < matrixLength; i++ {
for j := 0; j < matrixLength; j++ {
matrixA[i][j] = matrixA[i][j] + matrixB[j][i]
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
我们可以看到前面的速度会比后面的块10倍
BenchmarkMatrixCombination-8 16 67211689 ns/op
BenchmarkMatrixReversedCombination-8 3 480798925 ns/op
2
简单点说就是第一种方式访问时,我们的数据会缓存到cache line中,这样可以加快查找速度
参考:
CPU缓存体系对Go程序的影响 - SegmentFault 思否 (opens new window)
# Go内存对齐
什么是内存对齐
struct 中的字段顺序不同,内存占用也有可能会相差很大,比如下面这段代码
type T1 struct {
a int8
b int64
c int16
}
type T2 struct {
a int8
c int16
b int64
}
2
3
4
5
6
7
8
9
10
11
在 64 bit 平台上,T1 占用 24 bytes,T2 占用 16 bytes 大小;而在 32 bit 平台上,T1 占用 16 bytes,T2 占用 12 bytes 大小。可见不同的字段顺序,最终决定 struct 的内存大小,所以有时候合理的字段顺序可以减少内存的开销。
因为64位的平台上,我们每次读取都是按字节来读取的,一个字节就是8位,比如我们的T1,在64位系统就会因为padding导致多占用了内存
而T2因为内存对其了,所以比上面少了8个字节
你正在编写的代码在性能(CPU、Memory)方面有一定的要求
你正在处理向量方面的指令
某些硬件平台(ARM)体系不支持未对齐的内存访问
平台(移植性)原因:不是所有的硬件平台都能够访问任意地址上的任意数据。例如:特定的硬件平台只允许在特定地址获取特定类型的数据,否则会导致异常情况
性能原因:若访问未对齐的内存,将会导致 CPU 进行两次内存访问,并且要花费额外的时钟周期来处理对齐及运算。而本身就对齐的内存仅需要一次访问就可以完成读取动作
参考:
- 在 Go 中恰到好处的内存对齐 - 知乎 (zhihu.com) (opens new window)
- Golang 是否有必要内存对齐? - poslua | ms2008 Blog (opens new window)
# unsafe包
golang是一种静态的强类型的语言,所有的类型都是不能随意转换的,Go语言是不允许两个指针类型进行转换的。go官方是不推荐使用unsafe的操作因为它是不安全的,它绕过了golang的内存安全原则,容易使你的程序出现莫名其妙的问题,不利于程序的扩展与维护。但是在很多地方却是很实用。在一些go底层的包中unsafe包被很频繁的使用。
Go unsafe 包的使用 - SegmentFault 思否 (opens new window)
# gdb和pprof使用
gdb是用来调试go程序的,pprof是用于监控程序性能的
# sync.Pool原理
这个东西可以用来缓存对象,来减轻GC消耗
sync.Pool
是 sync 包下的一个组件,可以作为保存临时取还对象的一个“池子”。个人觉得它的名字有一定的误导性,因为 Pool 里装的对象可以被无通知地被回收,可能sync.Cache
是一个更合适的名字。
有啥用
对于很多需要重复分配、回收内存的地方,sync.Pool
是一个很好的选择。频繁地分配、回收内存会给 GC 带来一定的负担,严重的时候会引起 CPU 的毛刺,而 sync.Pool
可以将暂时不用的对象缓存起来,待下次需要的时候直接使用,不用再次经过内存分配,复用对象的内存,减轻 GC 的压力,提升系统的性能。
如何使用
package main
import (
"fmt"
"sync"
)
var pool *sync.Pool
type Person struct {
Name string
}
func initPool() {
pool = &sync.Pool {
New: func()interface{} {
fmt.Println("Creating a new Person")
return new(Person)
},
}
}
func main() {
initPool()
p := pool.Get().(*Person)
fmt.Println("首次从 pool 里获取:", p)
p.Name = "first"
fmt.Printf("设置 p.Name = %s\n", p.Name)
pool.Put(p)
fmt.Println("Pool 里已有一个对象:&{first},调用 Get: ", pool.Get().(*Person))
fmt.Println("Pool 没有对象了,调用 Get: ", pool.Get().(*Person))
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
深度解密 Go 语言之 sync.Pool - Stefno - 博客园 (cnblogs.com) (opens new window)