Go语言之性能优化 | 青训营笔记

46 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

前言

本文为作者参与青训营学习的课程总结,记录着我对课程内容的收获和思考,以便用于日后的复习查阅。如果所写内容出现不准确的表述或错误,欢迎各位在评论区友好指出。

本文介绍一些在使用Go语言编写代码时可能会踩的关于内存消耗方面的坑。

性能优化建议

切片预分配内存

尽可能在初始化切片时设置切片的容量大小

如果我们在创建一个切片时知道该切片至少需要多大的容量,最好就在make时指定。

image.png

我们知道切片在扩容时如果所需长度小于容量,直接在切片追加元素。可如果所需长度超过了容量,那就会重新分配一片更大的内容空间,将原切片复制到新切片再添加新元素。

添加的元素一旦多起来,正是这个可能存在的重新分配内容的操作会影响程序性能。同样的map创建也是可以通过预分配内存来减少内存拷贝和Rehash的消耗.

切片上创建切片

如果我们在一个原有的大切片的基础上创建一个小切片,是不会再创建一个小的底层数组的。毕竟切片是对数组片段的引用,所以这时候只是再创建一个数组的小引用

但存在这样一个问题: 我们需要的只是这个小的切片,但它引用着底层的大数组,这就导致这份大数组始终占着很大的内存空间,不会因为大切片的不使用而释放掉。

所以我们可以用copy代替re-slice,重新申请一份小的内存,而大的内存被释放掉。

字符串拼接

由于字符串在Go中是不可变类型,占用的内存大小是固定的,每次+拼接字符串都会重新分配内存,因此进行字符串拼接操作的时候不推荐这种拼接方式,而是用使用strings.Builder

Builder底层是一个byte数组,采用的是内存扩充策略,不需要每次重新分配内容,以此提高拼接性能.

atomic包

go语言通过内置的sync/atomic包提供对原子操作的支持

原子操作和互斥锁

锁的实现是通过操作系统来实现,属于系统调用

atomic操作是通过硬件实现,效率比锁高。

image.png 两者都能用于保证并发安全,只不过Mutex用于保护一段逻辑操作,而atomic用于保护对变量的更新。

当我们需要对一个变量进行并发保护的时候使用atomic是更有效率的选择。