Go - 基于逃逸分析来提升程序性能

前言

为什么需要了解逃逸分析?

因为我们想要提升程序性能,通过逃逸分析我们能够知道变量是分配到堆上还是栈上,如果分配到栈上,内存的分配和释放都是由编译器进行管理,分配和释放的速度非常快,如果分配到堆上,堆不像栈那样可以自动清理,它会引起频繁地进行垃圾回收(GC),而垃圾回收会占用比较大的系统开销。

什么是逃逸分析?

在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法,简单来说就是分析在程序的哪些地方可以访问到该指针。

简单的说,它是在对变量放到堆上还是栈上进行分析,该分析在编译阶段完成。如果一个变量超过了函数调用的生命周期,也就是这个变量在函数外部存在引用,编译器会把这个变量分配到堆上,这时我们就说这个变量发生逃逸了。

如何确定是否逃逸?

go run -gcflags '-m -l' main.go

可能出现逃逸的场景

01

package main

type Student struct {
    Name interface{}
}

func main()  {
    stu := new(Student)
    stu.Name = "tom"

}

分析结果:

go run -gcflags '-m -l' 01.go
# command-line-arguments
./01.go:8:12: new(Student) does not escape
./01.go:9:11: "tom" escapes to heap

interface{} 赋值,会发生逃逸,优化方案是将类型设置为固定类型,例如:string

package main

type Student struct {
    Name string
}

func main()  {
    stu := new(Student)
    stu.Name = "tom"

}

分析结果:

go run -gcflags '-m -l' 01.go
# command-line-arguments
./01.go:8:12: new(Student) does not escape

02

package main

type Student struct {
    Name string
}

func GetStudent() *Student {
    stu := new(Student)
    stu.Name = "tom"
    return stu
}

func main() {
    GetStudent()
}

分析结果:

go run -gcflags '-m -l' 02.go
# command-line-arguments
./02.go:8:12: new(Student) escapes to heap

返回指针类型,会发生逃逸,优化方案视情况而定。

函数传递指针和传值哪个效率高吗?我们知道传递指针可以减少底层值的拷贝,可以提高效率,但是如果拷贝的数据量小,由于指针传递会产生逃逸,可能会使用堆,也可能会增加 GC 的负担,所以传递指针不一定是高效的。

不要盲目使用变量指针作为参数,虽然减少了复制,但变量逃逸的开销可能更大。

03

package main

func main() {
    nums := make([]int, 10000, 10000)

    for i := range nums {
        nums[i] = i
    }
}

分析结果:

go run -gcflags '-m -l' 03.go
# command-line-arguments
./03.go:4:14: make([]int, 10000, 10000) escapes to heap

栈空间不足,会发生逃逸,优化方案尽量设置容量,如果容量实在过大那就没办法了。

小结

  1. 逃逸分析是编译器在静态编译时完成的。
  2. 逃逸分析后可以确定哪些变量可以分配在栈上,栈的性能好。

以上,希望对你能够有所帮助。

推荐阅读


新亮笔记
技术的深度和广度只能靠自己努力去发掘,谁也不能替你学习,在这里希望你能有所收获。

日拱一卒

2.9k 声望
1.2k 粉丝
0 条评论
推荐阅读
关于项目中 Repository 层的思考
维护这一层的开发者,可以称为 仓库管理员 ,当使用者需要查询数据的时候,需要告诉仓库管理员,由仓库管理员拿给他,至于仓库管理员从哪拿的数据,使用者无需关系。

新亮2阅读 1.8k评论 1

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木75阅读 7.1k评论 16

从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木45阅读 8.6k评论 6

从零搭建 Node.js 企业级 Web 服务器(二):校验
校验就是对输入条件的约束,避免无效的输入引起异常。Web 系统的用户输入主要为编辑与提交各类表单,一方面校验要做在编辑表单字段与提交的时候,另一方面接收表单的接口也要做足校验行为,通过前后端共同控制输...

乌柏木35阅读 6.7k评论 10

一文搞懂秒杀系统,欢迎参与开源,提交PR,提高竞争力。早日上岸,升职加薪。
前言秒杀和高并发是面试的高频考点,也是我们做电商项目必知必会的场景。欢迎大家参与我们的开源项目,提交PR,提高竞争力。早日上岸,升职加薪。知识点详解秒杀系统架构图秒杀流程图秒杀系统设计这篇文章一万多...

王中阳Go33阅读 2.5k评论 1

封面图
从零搭建 Node.js 企业级 Web 服务器(五):数据库访问
回顾 从零搭建 Node.js 企业级 Web 服务器(一):接口与分层,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,控制层与服务层实现了业务处理过程,模型层定义了业务实体并以 对象-关系...

乌柏木34阅读 5.1k评论 9

从零搭建 Node.js 企业级 Web 服务器(十三):断点调试与性能分析
Node.js 官方提供了断点调试机制,出于安全性考虑默认为关闭状态,可以通过 node 参数 --inspect 或 --inspect-brk 开启,配合 IDE 能够非常方便地调试代码,本章就上一章已完成的项目 licg9999/nodejs-server-ex...

乌柏木31阅读 4.2k评论 9

日拱一卒

2.9k 声望
1.2k 粉丝
宣传栏