首页 > 其他分享 >Go中的一些优化笔记,简单而不简单

Go中的一些优化笔记,简单而不简单

时间:2024-06-19 15:47:10浏览次数:12  
标签:map slice struct 简单 笔记 使用 Go 优化 我们

转自https://mp.weixin.qq.com/s/X8c6ZIJdBFptYA9CRj6wnA

今天小土给大家带来一篇关于 Golang 项目中最简单的优化的文章。原文见 Golang: simple optimization notes[1]

我们这里简单聊一下优化本身,然后我们直接从实际的示例开始。

为什么要优化呢?

当你资源占有较高的话会需要很大的成本,虽然现在服务器资源也不是很贵,但是你还是需要针对的做一些优化工作。

另外每个优化应该建立在一个benchmark的基础上,需要体现它给我们带来多大的收益。

下面主要从slice、string、struct、function、map、interface、channel、pointer等方面罗列了一些常见的优化点。

数组和slice优化篇
提前为slice分配内存
尽量使用第三个参数: make([]T, 0, len)

如果你事先不知道确切的数量并且slice是临时的,你可以设置得大一些,只要slice在运行时不会增长。

不要忘记使用“copy”
我们尽量不要在复制时使用 append,例如,在合并两个或多个slice时。

正确地使用迭代
如果我们有一个包含很多元素或比较大的元素的slice,我们会尝试使用“for”或 range 单个元素。通过这种方法,可以避免不必要的复制。

学会复用slice
如果我们需要对传入的slice进行某种操作并返回结果,我们可以直接return,但已经修改了。这样我们就可以避免了新的内存分配。

不要留下未使用的slice
如果我们需要从slice中切下一小块并仅使用它,其实主要部分也会保留下来。可以使用copy产生一个新的slice,而旧的对象让GC回收。

string-字符串优化篇
正确地进行拼接
如果拼接字符串可以在一个语句中完成,那么可以使用“+”,如果需要在循环中执行此操作,那么可以使用string.Builder。通过“Grow”也可以预先指定builder的大小。

使用转换优化
由于字符串是由字节组成的,因此有时这两种类型之间的转换可以避免内存分配。

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

避免内存分配
我们可以使用map来替代复合键,我们也可以使用[]byte。尽量不要使用fmt包,因为它的所有函数都使用了反射。

struct-结构体优化篇
避免复制大的struct
我们理解的小struct,是指不超过 4 个字段的struct,不超过一个机器字。

标准的copy案例

转换成interface
接收和发送到channel
替换map中的item
向slice添加元素
迭代(range)
避免通过指针来访问struct中的字段
解引用是比较昂贵的,我们可以尽量少做,尤其是在循环中。我们也会失去使用快速寄存器的能力。

使用小型的struct
这项工作由编译器优化的,这意味着它的工作量很小。

通过内存对齐来减小struct大小
我们可以对齐struct(根据字段的大小,以正确的顺序排列),从而可以减小struct本身的大小。

func-函数优化篇
使用内联函数或自己内联
我们尽量编写一些可供编译器内联的小函数——它很快,但自己从函数中嵌入代码则更快。对于热路径函数尤其如此。

什么情况下不会被内联?

recovery 函数
select
类型声明
defer
goroutine
for-range
明智地选择你的函数参数
我们尽量使用“小”参数,因为它们的拷贝会被特别优化。我们也尝试在拷贝和GC的负载的与增长堆栈之间保持平衡。避免使用大量的参数——让你的程序使用超快速的寄存器(寄存器的数量是有限的)

声明一个命名好的return结果
这似乎比在函数体中声明这些变量更高效。

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

谨慎使用“defer”
尽量不要使用 defer,或者至少 不要在循环中使用defer 。

为“hot path”提供便利
避免在这些地方分配内存,尤其是短期对象。首先要检查的的就是最常见的分支(if,switch)。

这里 hot path在Go源码中[2]也出现多次,根据在 sync.Once 的上下文中,“hot path”是什么意思?[3]中的回答,这里翻译为热路径是非常频繁执行的指令序列。

map优化篇
提前分配内存
一切都和其他地方一样。初始化map时,指定其大小。

使用空结构作为值
struct{}什么都不是,因此例如对信号值使用这种方法是非常有益的。

清空map
map只能增长,不能缩小。我们需要控制这一点——完全而明确地重置map。因为删除其所有元素无济于事。

尽量不要在键和值中使用指针
如果 map 不包含指针,那么 GC 就不会在它上面浪费宝贵的时间。而且要知道字符串也是指针——使用[]byte而不是字符串作为键。

减少更改的次数
同样,我们不想使用指针,但我们可以使用 map 和 slice 的复合体,并将键存储在 map 中,将可以不受限制地更改的值存储在slice中。

interface优化篇
计算内存分配
请记住,要给一个接口赋值,你首先需要将其拷贝到某处,然后粘贴一个指针。关键字是拷贝。事实证明,装箱和拆箱的成本将近似于结构体的大小和一次分配。

选择最佳的类型
在某些情况下,装箱/拆箱期间不会进行内存分配。例如,比较小的和布尔值的变量和常量、具有一个简单字段的struct、指针(包括map、chan、func)

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

仅在需要时使用
避免在小型、频繁调用的函数的参数和结果中使用接口。我们不需要额外的包装和拆包。减少使用接口方法调用的频率,哪怕只是因为它可以防止内联。

指针、chan、BCE(Bounds Check Elimination-边界检查) 优化篇
避免不必要的解引用
尤其是在循环中,因为事实证明它太昂贵了。解引用是我们不想以牺牲自己为代价执行的一系列必要操作。

channel使用效率是低效的
使用channel会比其他同步方法慢。另外,select 中的 case 越多,我们的程序就越慢。但是select、case + default是优化过了的。

尽量避免不必要的边界检查
这也很昂贵,我们应该尽一切可能避免它。例如,一次检查(获取)最大slice索引比多次检查更正确。最好是立即尝试获得极端的选项。

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

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

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

最后小土也祝你在优化的路上做到尽善尽美。

标签:map,slice,struct,简单,笔记,使用,Go,优化,我们
From: https://www.cnblogs.com/wzj4858/p/18256373

相关文章

  • 使用Gunicorn部署django项目
    安装pipinstallgunicorn配置配置Gunicorn的systemd服务文件创建Gunicorn服务文件:sudovim/etc/systemd/system/gunicorn.service添加以下内容到服务文件:[Unit]Description=gunicorndaemonAfter=network.target[Service]User=youruserGroup=www-dataWorki......
  • GLORY论文阅读笔记
    GoingBeyondLocal:GlobalGraph-EnhancedPersonalizedNewsRecommendations论文阅读笔记Abstract现存的问题:​ 近期的大多数工作主要侧重于使用先进的自然语言处理技术从丰富的文本数据中提取语义信息,并采用基于内容的方法从局部历史新闻中提取信息。然而,这种方法缺乏全局......
  • django admin管理中增加日志记录
    直接将代码复制到django项目中的admin.py中fromdjango.contribimportadmin#[email protected](admin.models.LogEntry)classLogEntryAdmin(admin.ModelAdmin):"""该类用于显示admin内置的django_admin_l......
  • 06《代码大全》阅读笔记
    《代码大全》是我在软件开发领域的一本必读书籍。这本书几乎涵盖了软件开发的方方面面,从编码到设计、测试到调试等各个环节都有详细的讲解和指导。首先,我被作者对于代码的重视所深深吸引。他在书中强调,代码质量决定了软件的可靠性和可维护性。好的代码应该易读、易懂、易维护。......
  • GSVA: Generalized Segmentation via Multimodal Large Language Models论文阅读笔记
    Motivation&AbsGeneralizedReferringExpressionSegmentation(GRES):相比于原始的RES任务,一个文本描述里可能出现多个需要分割的物体,或者没有需要分割的物体,难点在于建模不同实体之间复杂的空间关系,以及识别不存在的描述。现有的方法如LISA难以处理GRES任务,为此作者提出了GSV......
  • 阅读笔记:《代码大全》阅读笔记
     整个书籍分为三个主要部分:基础篇、结构篇和设计篇。这一结构合理而紧密,形成了一个有机的体系。基础篇从基本的编程原则入手,强调代码的可读性和可维护性。结构篇深入探讨了代码的组织结构和模块化,为开发者提供了构建大型系统的实践经验。设计篇则引领读者进入系统设计的复杂......
  • 阅读笔记《代码大全》阅读笔记
    首先,《代码大全》强调了软件构建的基本原则。它引导读者深入了解模块化的重要性,让代码更易于管理和理解。清晰性和可维护性也是其关注的焦点,因为清晰易读的代码不仅有助于减少错误,还能提高团队合作效率。其次,书中深入探讨了代码质量。McConnell认为,写出高质量的代码是至关重要......
  • [AWS]EKS简单实战(二)ingress根据路径转发到不同的pod
    背景:在上一次实验中是利用ALB类型的service转到转发流量到后端的POD。采用的架构如下:但是在实际使用过程中,如果每一个POD都需要一个ALB去进行转发,那将采用大量的资源,所以这里要引入ingress,使得外部流量先达到ingress,然后ingress再根据具体的后缀去进行转发到不同的pod中,架构如......
  • OAuth2.0的简单认识
    内容:结合实际平台应用,以开发者视角简单描述OAuth2.0OAuth2.0其实是一种授权机制,而且在日常的互联网环境中是非常常见的,比如一些网站或者应用,在我们选择登录时,常常会有第三方登录选项,其实这就是OAuth2.0的一种实现。官方协议流程这里直接引用官网文档流程授权码模式其实OAu......
  • 《梦断代码》阅读笔记
    《梦断代码》一书深刻描绘了软件开发领域的种种问题和挑战,强调了软件开发不仅是技术活动,更是艺术与科学的结合体。在软件开发过程中,除了要具备技术上的精湛,还需要团队合作、沟通协调、创新思维等综合能力。一个成功的软件项目离不开对艺术与科学的深刻理解和应用,只有深入研究技术......