一步两步是魔鬼的步伐

Duan1v's Blog

GO性能追踪trace+pprof

trace单干

  • 以两种斐波那契数列的计算方式做对比
 1
 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"runtime/trace"
	"sync"
)

func FbnqDp(n int) (s int) {
	dp := make([]int, n+1)
	dp[1] = 1

	for i := 2; i < n+1; i++ {
		dp[i] = dp[i-2] + dp[i-1]
	}

	return dp[n]
}

func Fbnq(n int) (s int) {
	if n <= 2 {
		return 1
	}
	return Fbnq(n-1) + Fbnq(n-2)
}

func main() {
	runtime.GOMAXPROCS(1)
	dir, err := os.Getwd()
	if err != nil {
		fmt.Println("获取目录路径失败!")
	}

	fileName := filepath.Join(dir, "test.pprof")
	f, _ := os.Create(fileName)
	trace.Start(f)
	defer trace.Stop()

	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer wg.Done()
		fmt.Println(Fbnq(40))
	}()

	go func() {
		defer wg.Done()
		fmt.Println(FbnqDp(40))
	}()
	wg.Wait()
}
1
go run main.go
  • 可以发现项目目录下生成了 test.pprof
1
go tool trace test.pprof
  • 弹出浏览器
https://static.duan1v.top/images/20220701172440.png
trace
  • 点击 Goroutine analysis ,查看两个go协程的性能
  • 对于暴力递归写的斐波那契函数:
https://static.duan1v.top/images/20220701164033.png
trace
  • 对于dp写的斐波那契函数,可以看出就一个栈:
https://static.duan1v.top/images/20220701162704.png
trace

配合pprof

  • 注意手动加上 _ "net/http/pprof"
 1
 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package main

import (
	"fmt"
	"log"
	"net/http"
	_ "net/http/pprof"
	"os"
)

func FbnqDp(n int) (s int) {
	dp := make([]int, n+1)
	dp[1] = 1

	for i := 2; i < n+1; i++ {
		dp[i] = dp[i-2] + dp[i-1]
	}

	return dp[n]
}

func Fbnq(n int) (s int) {
	if n <= 2 {
		return 1
	}
	return Fbnq(n-1) + Fbnq(n-2)
}

func main() {
	go func() {
		if err := http.ListenAndServe(":6060", nil); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}()

	http.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte("Hello World!"))
	})
	http.HandleFunc("/Fbnq", func(w http.ResponseWriter, req *http.Request) {
		fmt.Println(Fbnq(10))
	})
	http.HandleFunc("/FbnqDp", func(w http.ResponseWriter, req *http.Request) {
		fmt.Println(FbnqDp(10))
	})
	http.ListenAndServe(":8080", nil)
}
  • 做如下操作:
https://static.duan1v.top/images/20220701165508.png
trace
  • 命令明细
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
➜  p9 go run main.go

# 20s是抓取时长,这期间的访问都会被记录到
➜  p9 curl http://127.0.0.1:6060/debug/pprof/trace\?seconds\=20 > trace.out

# 所以下面的命令需要在上面的命令执行后的20s内执行
➜  p9 curl 127.0.0.1:8080/hello 
Hello World!%                                 
➜  p9 curl 127.0.0.1:8080/Fbnq  
➜  p9 curl 127.0.0.1:8080/FbnqDp

➜  p9 go tool trace trace.out 
  • 点击 Scheduler latency profile
https://static.duan1v.top/images/20220701172112.png
trace

GO逃逸分析

栈和堆

  • 分配快,栈分配内存只需要两个CPU指令:“PUSH”和“RELEASE”,随着函数的调用分配和回收
  • 从高位到低位存放函数等数据
  • 查看栈大小
1
2
➜  ~ ulimit -a | grep stack
-s: stack size (kbytes)             8192

  • 分配慢,需要使用new方法申请分配,需要GC回收
  • 从低位到高位存放数据

逃逸分析

概述

  • 是谁进行的:go编译器
  • 为什么而进行:合理化内存分配的位置
  • 如何进行:内存逃逸
  • 逃逸的标准:当前变量是否可以只在声明的作用域中
  • 是好是坏:自我优化的过程,肯定是好的啊(一开始听到逃逸这个词,总感觉不是啥好东西)
  • 有逃逸分析,在编写代码时是否还要注意内存分配:当然需要,应当尽量利用栈分配

查看逃逸分析

使用go build:

1
go build -gcflags="-m -m -l" main.go
  • -m 显示优化决策
  • -l 禁止使用内联

使用反汇编:

1
go tool compile -S main.go

哪些情况发生逃逸

  • 局部变量获取引用
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package main

type S struct {
	M *int
}

func main() {
	var i int
	refStruct(i)
	refStruct1(&i)
}

func refStruct1(y1 *int) (z S) {
	z.M = y1
	return z
}
func refStruct(y int) (z S) {
	z.M = &y
	return z
}
1
2
3
4
5
6
7
8
9
➜  p8  go run -gcflags '-m -m -l' main.go
# command-line-arguments
# ...
./main.go:17:16: y escapes to heap:
./main.go:17:16:   flow: z = &y:
./main.go:17:16:     from &y (address-of) at ./main.go:18:8
./main.go:17:16:     from z.M = &y (assign) at ./main.go:18:6
./main.go:17:16: moved to heap: y
# ...
  • 变量大小不确定及栈空间不足引发逃逸
 1
 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
31
32
33
package main

import (
	"math/rand"
)

func LessThan8192() {
	nums := make([]int, 100) // = 64KB
	for i := 0; i < len(nums); i++ {
		nums[i] = rand.Int()
	}
}

func MoreThan8192() {
	nums := make([]int, 1000000) // = 64KB
	for i := 0; i < len(nums); i++ {
		nums[i] = rand.Int()
	}
}

func NonConstant() {
	number := 10
	s := make([]int, number)
	for i := 0; i < len(s); i++ {
		s[i] = i
	}
}

func main() {
	NonConstant()
	MoreThan8192()
	LessThan8192()
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
➜  p8 go build -gcflags '-m -m -l' main.go
# command-line-arguments
./main.go:8:14: make([]int, 100) does not escape
./main.go:15:14: make([]int, 1000000) escapes to heap:
./main.go:15:14:   flow: {heap} = &{storage for make([]int, 1000000)}:
./main.go:15:14:     from make([]int, 1000000) (too large for stack) at ./main.go:15:14
./main.go:15:14: make([]int, 1000000) escapes to heap
./main.go:23:11: make([]int, number) escapes to heap:
./main.go:23:11:   flow: {heap} = &{storage for make([]int, number)}:
./main.go:23:11:     from make([]int, number) (non-constant size) at ./main.go:23:11
./main.go:23:11: make([]int, number) escapes to heap
  • 指针嵌套导致的逃逸分析失败
 1
 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
package main

type S struct {
	M *int
}

type S1 struct {
	M int
}

func main() {
	var x S
	var i int
	var i1 int
	ref(&i, &x)
	var x1 S1
	ref1(i1, &x1)
	ref2(&i1, x)
}

func ref(y *int, z *S) {
	z.M = y
}

func ref1(y int, z *S1) {
	z.M = y
}
func ref2(y *int, z S) {
	z.M = y
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
➜  p8 go build -gcflags '-m -m -l' main.go
# command-line-arguments
./main.go:21:10: parameter y leaks to {heap} with derefs=0:
./main.go:21:10:   flow: {heap} = y:
./main.go:21:10:     from z.M = y (assign) at ./main.go:22:6
./main.go:21:10: leaking param: y
./main.go:21:18: z does not escape
./main.go:25:18: z does not escape
./main.go:28:11: y does not escape
./main.go:28:19: z does not escape
./main.go:13:6: i escapes to heap:
./main.go:13:6:   flow: {heap} = &i:
./main.go:13:6:     from &i (address-of) at ./main.go:15:6
./main.go:13:6:     from ref(&i, &x) (call parameter) at ./main.go:15:5
./main.go:13:6: moved to heap: i

一些优化

  • 以指针传参,比如下面这个就会引起内存逃逸
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

type S struct{}

func main() {
	var x S
	y := x
	_ = *identity(y)
}

func identity(z S) *S {
	return &z
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
➜  p8  go run -gcflags '-m -m -l' main.go
# command-line-arguments
./main.go:11:15: parameter z leaks to ~r0 with derefs=0:
./main.go:11:15:   flow: ~r0 = &z:
./main.go:11:15:     from &z (address-of) at ./main.go:12:9
./main.go:11:15:     from return &z (return) at ./main.go:12:2
./main.go:11:15: z escapes to heap:
./main.go:11:15:   flow: ~r0 = &z:
./main.go:11:15:     from &z (address-of) at ./main.go:12:9
./main.go:11:15:     from return &z (return) at ./main.go:12:2
./main.go:11:15: moved to heap: z

可以优化成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

type S struct{}

func main() {
	var x S
	y := &x
	_ = *identity(y)
}

func identity(z *S) *S {
	return z
}
1
2
3
4
5
6
➜  p8  go run -gcflags '-m -m -l' main.go
# command-line-arguments
./main.go:11:15: parameter z leaks to ~r0 with derefs=0:
./main.go:11:15:   flow: ~r0 = z:
./main.go:11:15:     from return z (return) at ./main.go:12:2
./main.go:11:15: leaking param: z to result ~r0 level=0
  • 无必要不return;下面这种就没必要让编译器进行一次逃逸
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

type S struct {}

func main() {
    var x S
    _ = *ref(x)
}

func ref(z S) *S {
    return &z
}

本文参考

网站备案

备案类型

IPC备案

  • 不支持个人申请,需要有资质的服务器供应商,比如阿里云,腾讯云;

  • 服务器在国内,不备案是不行的,毕竟服务器供应商想停就停;

  • 做这个备案主要是为了网站可以被访问,所以国内服务器的网站是要做的;

  • 七牛云的国内加速域名也需要IPC备案。

公安网备案

  • 一般是基于IPC备案的,因为
法规
根据《计算机信息网络国际联网安全保护管理办法》(公安部令第33号)第二十三条规定“违反本办法第十一条、第十二条规定,不履行备案职责的,由公安机关给予警告或者停机整顿不超过六个月的处罚。
  • 所以说服务器在境外,只有被墙的风险;
  • 公安网备案有利于对网站的检举监督,网安估摸着是想所有网站都去公安备案,我一境外服务器也让我备案(一开始不懂,申请了,都撤回了,还电话让我备案),不过也无所谓,放在底下,当作是网站的门神了 😅 。

博客备案

github page或者 netlify等部署想要使用七牛云怎么办

Note
对于这些无服务器或者说未购买服务器的博客,域名是肯定要的,但是光有域名,也只能使用境外的CDN,所以域名要进行IPC备案

如果说在腾讯云只买了域名能进行IPC备案吗?

Note

客服回答,IPC备案与域名在哪无关,要去服务器供应商平台申请;

但是腾讯云的IPC备案页面有个 备案授权码 ,这个只有企业服务器是有的,但是是可以买到的(某宝),再但是记一下价格;

阿里云也有类似的,备案服务号 ,也是可以买到了,对比一下价格;

所以说,自搭博客的域名不要在腾讯买!买域名前先查下相应平台的备案服务号价格!

本地博客编写

博客自搭后传

好了,博客是搭好了,该怎么更舒服地进行本地编写呢?

  • 主要的是编写时图片怎么处理
  • 之后是已经编写完的文章的图片怎么处理

坚果云

直接使用坚果云的轻应用 坚果云Markdown

  • 但是会创建额外的文件夹,看着不太舒服
  • 用这种方式,注意在.gitignore中忽略该目录
  • 对于之前创建的文章的图片,处理比较麻烦,而且直接用云存的话,识别的图片格式不多

Typora + PicGo

具体配置

使用

  • 将图片复制到Typora中,右键+上传图片

处理过往文章中的图片

  • 策略是先右键+复制到本地+上传图片
  • 过往文章少可以手动点点,我的比较少
  • 多的话python应该可以处理吧也许……

图床的选择

SMMS

  • 5 MB max per file. 10 files max per request.
  • 最大只有5G的存储,可以当副图床

七牛云

  • 需要域名
  • 国内服务器的CDN,必须是备案的域名
  • 非国内的访问极慢
  • 若是东南亚的服务器,picgo中的确认存储区域填 as0

笑果图床

  • 还有很多类似的网页图床,上传一次,有不同尺寸的图片
  • 但是picgo不支持

Github | Gitee + Jsdelivr

Amazon CloudFront + Amazon S3

  • 我目前使用的图床
  • picgo需要搜索插件 S3

系统换成了Win11

小结

  • win11的UI比win10好很多

  • 最近先换系统,再搭博客,有点乏,就简单记录下

常用工具获取

笔记

笔记本

同步工具

密码

密码本

同步工具

WSL2

先搭建所需环境

将基本的软件安装完之后,导出打个包,备份以备换电脑时不用再次搭建

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 导出
wsl --export Ubuntu-20.04 "H:\Wsl2\ubuntu_20.04.tar"

# 注册
wsl --import Ubuntu_20.04 "H:\Wsl2\data" "H:\Wsl2\ubuntu_20.04.tar" --version 2

# 注销
wsl --unregister Ubuntu_20.04

# 进入系统,可以配合Windows Terminal
wsl --distribution Ubuntu_20.04 --user 用户名

上云

  • 导出的WSL2大小超过5个G了,白嫖的云存储,我只发现了Google Drive

zsh&&git卡慢

1
2
3
4
5
6
7
8
git config --global --add oh-my-zsh.hide-dirty 1
git config --global --add oh-my-zsh.hide-status 1
# 移除配置
# git config --remove-section oh-my-zsh
# 当前仓库
git config --local -e
# 全局设置
git config --global -e

动态桌面

  • steam上的造物主壁纸

  • 不想steam随机开启,我先把壁纸下载下来,然后用了Lively Wallpaper

其他问题

无法使用outlook作为登录账户

  • 将隐私与安全中的位置打开,不然登不了,全网找不到答案,挺折腾的就

使用Hugo自搭博客系列四:部署

手动部署

参考github page

  • 注意,git仓库需要是public的;下面自动的不用

自动部署

Travis CI官网

Netlify官网

Vercel

AWS Amplify

Netlify

参考Netlify部署Hugo个人博客Hugo Algolia搜索及Netlify自动化处理 ,这位博主写的很详细了,我就不写了。

AWS Amplify

  • 我目前换成了这个来部署,之前使用的Netlify,总是间歇性卡顿,很卡的那种

  • 关于亚马逊云的使用还是有点麻烦的,亚马逊中国只支持企业,个人体验需要用注册海外账户

https://static.duan1v.top/images/2b7b0f90a0ee37d6cc115e541fdc18ba.png
博客自动部署
  • 相比于Netlify,直接使用的不是extended,这会导致样式加载不生效,可以使用下面贴的 amplify.yml

  • 其他的环境变量和上面的Netlify差不多,将 HUGO_VERSION 改为 VERSION_HUGO

Vercel

  • AWS Amplify的工作人员打电话给我,说服务都是免费一年的;我心想,我可是想着白嫖永久的;好吧,那就清理资源,销号

  • 转头Vercel,折腾过上面两个之后,也很简单;

  • 目前我的图床、评论、博客都是放在github上的,由Vercel部署的

贴下几个文件

  • 根目录下的.gitignore
1
2
3
4
.env
node_modules
.idea
.vscode
  • 根目录下的netlify.toml
 1
 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
31
32
33
34
35
[build]
publish = "public"
command = "hugo --gc --minify --theme=even && npm install atomic-algolia --save && npm run algolia"

[context.production.environment]
HUGO_VERSION = "0.101.0"
HUGO_ENV = "production"
HUGO_ENABLEGITINFO = "true"

[context.split1]
command = "hugo --gc --minify --enableGitInfo"

[context.split1.environment]
HUGO_VERSION = "0.101.0"
HUGO_ENV = "production"

[context.deploy-preview]
command = "hugo --gc --minify --buildFuture -b $DEPLOY_PRIME_URL"

[context.deploy-preview.environment]
HUGO_VERSION = "0.101.0"

[context.branch-deploy]
command = "hugo --gc --minify -b $DEPLOY_PRIME_URL"

[context.branch-deploy.environment]
HUGO_VERSION = "0.101.0"

[context.next.environment]
HUGO_ENABLEGITINFO = "true"

[[redirects]]
from = "/npmjs/*"
to = "/npmjs/"
status = 200
  • public目录下的.gitignore
1
2
*
!.gitignore
  • amplify.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
version: 1
frontend:
  phases:
    preBuild:
      commands:
        - wget https://github.com/gohugoio/hugo/releases/download/v${VERSION_HUGO}/hugo_extended_${VERSION_HUGO}_Linux-64bit.tar.gz
        - tar --overwrite -xf hugo_extended_${VERSION_HUGO}_Linux-64bit.tar.gz hugo
        - mv hugo /usr/bin/hugo
        - rm -rf hugo_extended_${VERSION_HUGO}_Linux-64bit.tar.gz
        - hugo version
        - hugo --gc --minify --buildFuture 
        - npm install atomic-algolia --save 
    build:
      commands:
        - hugo --gc --minify --theme=loveit --enableGitInfo=false 
        - npm run algolia
  artifacts:
    baseDirectory: public
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*