0

0

Go:简单的优化笔记

Go语言进阶学习

Go语言进阶学习

发布时间:2023-07-21 13:04:42

|

1021人浏览过

|

来源于Go语言进阶学习

转载

在云计算时代,我们经常创建 Serverless 应用(一种云原生开发模式,允许开发人员构建和运行应用程序,而无需管理服务器)。当我们的项目采用这种模式,那基础设施维护预算将排在首位。如果我们的服务负载很低,它实际上近乎是免费的。但是如果出现问题,你将为此付出很多!当谈到金钱时,你肯定会以某方式对它做出反应。

当你的 VPS 运行着多个服务应用,但其中一个有时会占用所有的资源,以至于都无法通过 ssh 访问服务器。你转到使用 Kubernetes 集群,为所有应用程序设置限制。随后看到一些应用程序被重新启动,因为 OOM-killer 解决了内存”泄漏“问题。

当然, OOM 并不总是泄漏问题,也可能是资源超支。泄漏问题大概率是由程序错误引起的,我们今天谈论的主题是如何尽量避免这种情况。

过多的资源消耗会伤害钱包,这意味着我们需要立即采取行动。

不要过早优化

现在让我们谈谈优化。希望你能明白为什么我们不要过早优化!

  • 第一,优化可能是无用的工作。因为我们应该先研究整个应用程序,而你的代码很可能不会成为瓶颈。我们需要的是快速的结果,MVP(Minimum Viable Product,最简可行产品),然后才会考虑它的问题。
  • 第二,优化都必须有所依据。也就是说,每次优化都应该建立在基准上,我们必须证明它给我们带来了多少利润。
  • 第三,优化也许会带来复杂。你需要知道的是,大多数优化会使代码的可读性变差。你需要把握好这种平衡。

优化建议

现在我们按照 Go 中的标准实体分类,来给出一些实用建议。

1. 数组与切片

提前为切片分配内存

尽量使用第三个参数:make([]T, 0, len)

如果不知道元素确切的数量并且切片是短暂的,可以分配更大一点,保障切片在运行时不会增长。

不要忘记使用 copy

尽量不要在复制时使用 append,例如在合并两个或多个切片时。

正确迭代

一个包含许多元素或大元素的切片,使用 for 去获取单个元素。通过这种方法,将避免不必要的复制。

复用切片

如果对传入的切片进行某种操作并返回已经修改的结果,我们可以返回它。这样能避免新的内存分配。

不要留下不使用的切片部分

如果需要从切片中切下一小块并仅使用它,该切片的主要部分也将被保留。正确的做法是,为这小块切片使用新的副本,而将旧的切片扔给 GC。

2. 字符串

正确拼接

如果拼接字符串可以在一个语句中完成,那就使用 + 操作符。如果需要在循环中执行此操作,使用 string.Builder,并使用它的 Grow 方法预先指定 Builder 的大小,减少内存分配次数。

转换优化

string 和 []byte 在底层结构上非常相近,有时这两种类型之间可以通过强转换来避免内存分配。

字符串驻留

可以池化字符串,从而帮助编译器只存储一次相同的字符串。

避免分配

我们可以使用 map(级联)而不是复合键,我们可以使用字节切片。尽量不使用 fmt 包,因为它所有的方法都用到了反射。

3. 结构体

避免拷贝大结构体

我们理解的小结构体是不超过4个字段不超过一个机器字大小。

一些典型的拷贝场景

  • 投射到 interface
  • 通道的接收和发送
  • 替换 map 中的元素
  • 向切片添加元素
  • 迭代(range)
避免通过指针访问结构体字段

解引用是昂贵的,我们应该尽可能少地这样做,尤其是在循环中。同时它也失去了使用快速寄存器的能力。

处理小结构体

这项工作由编辑器进行优化,这意味着它很便宜。

使用对齐减小结构体大小

我们可以对齐结构体(根据字段的大小,以正确的顺序排列它们),以此减小结构体本身的大小。

4. 函数

使用内联函数或自己内联它们

尝试编写可供编译器内联的小函数,它会很快,甚至快过自己在函数中嵌入代码。对于热路径(hot path)尤其如此。

哪些不会内联

  • recovery
  • select 块
  • 类型声明
  • defer
  • goroutine
  • for-range
合理地选择函数参数

尝试使用小参数,因为它们的复制将被优化。尝试复制和栈增长在GC负载保持平衡。避免大量参数,让你的程序使用快速寄存器(它们的数量是有限的)。

命名返回值

这似乎比在函数体中声明这些变量更高效。

保存中间结果

帮助编译器优化你的代码,保存中间结果,然后会有更多的选项来优化你的代码。

仔细地使用 defer

尽量不要使用 defer,或者至少不要在循环中使用它。

助力 hot path

避免在热路径分配内存,尤其是短生命对象。制作最常见分支(if,switch)

5. Map

提前分配内存

和 slice 一样,初始化 map 时,指定其大小。

使用空结构体为值

struct{} 什么都不是(不占内存),因此例如传递信号时,使用它是非常有益的。

清空 map

map 只能增长,不能缩小。我们需要重置 map 时,删除其所有元素是无济于事的。

尽量不在键和值中使用指针

如果 map 中不包含指针,那么 GC 就不会在上面浪费宝贵的时间。字符串也使用了指针,因此应该使用字节数组而不是字符串作为键。

减少修改次数

同样,我们不想使用指针,但我们可以使用  map 和 slice 的组合,将键存储在 map 中,将值存在 slice。这样我们就可以不受限制地更改值。

6. Interface

计算内存分配

请记住,要为接口分配值时,首先需要将其复制到某处,然后将指针黏贴给它。关键是复制。事实证明,接口的装箱和拆箱的成本将近似于结构体大小的一次分配。

选择最优类型

在某些情况下,接口的装箱和拆箱期间没有分配。例如,变量和常量的小值或布尔值、具有一个简单字段的结构体、指针(包括 map、channel、func)

避免内存分配

与其他地方一样,尽量避免不必要的分配。例如将一个接口分配给另一个接口,而不是装箱两次。

仅在需要时使用

避免在频繁调用的函数参数和返回结果中使用接口。我们不需要额外的拆装包操作。减少使用接口方法调用的频率,因为它会阻止内联。

7. 指针、通道、边界检查

避免不必要的解引用

尤其是在循环中,因为事实证明它太昂贵了。解引用是我们不想自费执行的操作。

使用通道效率低下

channel 同步比其他同步原语方法慢。另外, select 中的 case 越多,我们的程序就越慢。但是,select,case 加 default 有被优化。

避免不必要的边界检查

这也很昂贵,我们应该避免它。例如,只检查(获取)一次最大切片索引,而不是多次。最好立即尝试获得极端选项。

总结

在整篇文章中,我们看到了一些相同的优化规则。

帮助编译器做出正确的决定,它会感谢你的。在编译时分配内存,使用中间结果,并尽量保持你的代码可读。

我再次重申,对于隐式优化,基准是强制性的。如果因为编译器在不同版本之间变化太快,昨天工作的东西明天就不能工作,反之亦然。

不要忘记使用 Go 内置的分析和跟踪工具。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

312

2023.08.02

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1435

2023.10.24

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

711

2023.08.22

switch语句用法
switch语句用法

switch语句用法:1、Switch语句只能用于整数类型,枚举类型和String类型,不能用于浮点数类型和布尔类型;2、每个case语句后面必须跟着一个break语句,以防止执行其他case的代码块,没有break语句,将会继续执行下一个case的代码块;3、可以在一个case语句中匹配多个值,使用逗号分隔;4、Switch语句中的default代码块是可选的等等。

518

2023.09.21

Java switch的用法
Java switch的用法

Java中的switch语句用于根据不同的条件执行不同的代码块。想了解更多switch的相关内容,可以阅读本专题下面的文章。

403

2024.03.13

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

248

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

205

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1435

2023.10.24

桌面文件位置介绍
桌面文件位置介绍

本专题整合了桌面文件相关教程,阅读专题下面的文章了解更多内容。

0

2025.12.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.1万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号