golang unmarshal & marshal 导致 cup 内存占用率过高

golang unmarshal & marshal 导致 cup 内存占用率过高

现象

当任务过多时,导致中控 cup 100%。

排查问题发现:

当任务数小于 2000 时:

请添加图片描述

当任务数大于 7000 时:

请添加图片描述

分析原因:

func fn(){
	res := initRes()
	result := make(map[string]*TranscodeEntity)
	for k, v := range res {
		taskId := k
		taskEntity := &TranscodeEntity{}
		err := json.Unmarshal(v, taskEntity)
		if err != nil {
			continue
		}
		result[taskId] = taskEntity
	}
}

每次分发任务时,都会查询所有任务,导致 Unmarshal 。结合线上内存分析工具 ppor 显示,当 cup 过高时,marshal & unmarshal 都会各自达到 30% 左右。

且分发任务执行次数频率较高,每分钟执行一次。

验证

生成初使数据

type TranscodeEntity struct {
	Weight    int    `json:"weight"`    // 任务权重
	TaskId    string `json:"taskId"`    //任务id
	...
    Profile  string `json:"perfile"` 
}

var mark = `{
"weight": 100,
"taskId": "task001",
...
"profile": "high"
}`

func initRes() map[string][]byte {
	res := make(map[string][]byte)
	for i := 0; i < Max; i++ {
		generateUUID, err := uuid.GenerateUUID()
		if err != nil {
			return nil
		}
		res[generateUUID] = []byte(mark)
	}
	return res
}

可能有问题的代码块

func willRefactor() {
	res := initRes()
	result := make(map[string]*TranscodeEntity)
	for k, v := range res {
		taskId := k
		taskEntity := &TranscodeEntity{}
		err := json.Unmarshal(v, taskEntity)
		if err != nil {
			continue
		}
		result[taskId] = taskEntity
	}
}
验证执行时间和内存
func BenchmarkWillRefactor(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		willRefactor()
	}
	b.StopTimer()

	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)
	fmt.Printf("Memory used: %v bytes\n", memStats)
}

结果

goos: darwin
goarch: arm64
pkg: study_go/feature_test
BenchmarkWillRefactor
Memory Usage: 31029672 bytes/op
Memory Usage: 34238947 bytes/op
Memory Usage: 61603173 bytes/op
BenchmarkWillRefactor-8   	      10	 109_089_525 ns/op

注:

61603173 bytes = 60 mb

109_089_525 ns = 0.1 s


使用更高效的 unmarshal 工具试试
func RefactorFn() {
	res := initRes()
	result := make(map[string]*TranscodeEntity)
	for k, v := range res {
		taskId := k
		taskEntity := &TranscodeEntity{}
		err := jsoniter.Unmarshal(v, taskEntity)
		if err != nil {
			continue
		}
		result[taskId] = taskEntity
	}
}
验证运行时间和内存使用
func BenchmarkRefactorFn(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		RefactorFn()
	}
	b.StopTimer()

	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)
	fmt.Printf("Memory Usage: %v bytes/op\n", int64(memStats.TotalAlloc)/int64(b.N))
}

结果

goos: darwin
goarch: arm64
pkg: study_go/feature_test
BenchmarkRefactorFn
Memory Usage: 32632032 bytes/op
Memory Usage: 34434436 bytes/op
Memory Usage: 53584024 bytes/op
BenchmarkRefactorFn-8   	      26	  45_508_535 ns/op

优化字段方案
type ShortTranscodeEntity struct {
	Weight    int    `json:"weight"`    // 任务权重
	TaskId    string `json:"taskId"`    //任务id
	TaskName  string `json:"taskName"`  //视频标题
	TransType int    `json:"transType"` //转码类型   0 轮播 1点播
}

var shortMark = `{
"weight": 100,
"taskId": "task001",
"taskName": "example video",
"streamId": "stream001",
}`

func initResShort() map[string][]byte {
	res := make(map[string][]byte)
	for i := 0; i < Max; i++ {
		generateUUID, err := uuid.GenerateUUID()
		if err != nil {
			return nil
		}
		res[generateUUID] = []byte(shortMark)
	}
	return res
}
只减少字段数据,使用 json 解析
func willRefactorShort() {
	res := initResShort()
	result := make(map[string]*TranscodeEntity)
	for k, v := range res {
		taskId := k
		taskEntity := &TranscodeEntity{}
		err := json.Unmarshal(v, taskEntity)
		if err != nil {
			continue
		}
		result[taskId] = taskEntity
	}
}

func BenchmarkWillRefactorShort(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		willRefactorShort()
	}
	b.StopTimer()

	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)
	fmt.Printf("Memory Usage: %v bytes/op\n", int64(memStats.TotalAlloc)/int64(b.N))
}

结果

goos: darwin
goarch: arm64
pkg: study_go/feature_test
BenchmarkWillRefactorShort
Memory Usage: 12021184 bytes/op
Memory Usage: 12005521 bytes/op
Memory Usage: 21302302 bytes/op
BenchmarkWillRefactorShort-8   	      81	  14_186_038 ns/op

修改字段,改变 struct,使用 json 解析

func willRefactorShort() {
	res := initResShort()
	result := make(map[string]*ShortTranscodeEntity)
	for k, v := range res {
		taskId := k
		taskEntity := &ShortTranscodeEntity{}
		err := json.Unmarshal(v, taskEntity)
		if err != nil {
			continue
		}
		result[taskId] = taskEntity
	}
}

func BenchmarkWillRefactorShort(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		willRefactorShort()
	}
	b.StopTimer()

	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)
	fmt.Printf("Memory Usage: %v bytes/op\n", int64(memStats.TotalAlloc)/int64(b.N))
}

结果

goos: darwin
goarch: arm64
pkg: study_go/feature_test
BenchmarkWillRefactorShort
Memory Usage: 7692352 bytes/op
Memory Usage: 7671502 bytes/op
Memory Usage: 11377431 bytes/op
BenchmarkWillRefactorShort-8   	      87	  13_443_757 ns/op
改变解析工具,只减少数据
func RefactorFnShort() {
	res := initResShort()
	result := make(map[string]*TranscodeEntity)
	for k, v := range res {
		taskId := k
		taskEntity := &TranscodeEntity{}
		err := jsoniter.Unmarshal(v, taskEntity)
		if err != nil {
			continue
		}
		result[taskId] = taskEntity
	}
}

func BenchmarkRefactorFnShort(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		RefactorFnShort()
	}
	b.StopTimer()

	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)
	fmt.Printf("Memory Usage: %v bytes/op\n", int64(memStats.TotalAlloc)/int64(b.N))
}

结果

goos: darwin
goarch: arm64
pkg: study_go/feature_test
BenchmarkRefactorFnShort
Memory Usage: 17038000 bytes/op
Memory Usage: 17168781 bytes/op
Memory Usage: 30109440 bytes/op
BenchmarkRefactorFnShort-8   	      58	  19_926_978 ns/op
改变解析工具,改变 struct
func RefactorFnShort() {
	res := initResShort()
	result := make(map[string]*ShortTranscodeEntity)
	for k, v := range res {
		taskId := k
		taskEntity := &ShortTranscodeEntity{}
		err := jsoniter.Unmarshal(v, taskEntity)
		if err != nil {
			continue
		}
		result[taskId] = taskEntity
	}
}

func BenchmarkRefactorFnShort(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		RefactorFnShort()
	}
	b.StopTimer()

	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)
	fmt.Printf("Memory Usage: %v bytes/op\n", int64(memStats.TotalAlloc)/int64(b.N))
}
goos: darwin
goarch: arm64
pkg: study_go/feature_test
BenchmarkRefactorFnShort
Memory Usage: 12674312 bytes/op
Memory Usage: 12746981 bytes/op
Memory Usage: 21715815 bytes/op
BenchmarkRefactorFnShort-8   	      62	  18_712_932 ns/op

结论

使用 jsoniter.Unmarshal有效的缩短了运行时长,但是内存使用情况,并没有优化。

所以要优化内存使用情况,需要修改查询手段:

  • 使用中间价保存任务信息(redis 等内存数据库)

  • 使用分页查询,限制查询数据大小

  • 优化数据字段,保存较少数据

  • json 大小,较大时,执行效率 json <= jsoniter,内存使用基本无差距

  • json 大小,较小时,执行效率 json >= jsoniter,内存使用 json < jsoniter

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值