点击上方蓝色“Go语言中文网”关注我们,领全套Go资料,每天学习 Go 语言

本文作者:鸟窝 smallnest,原文有视频讲解

原文链接:https://colobu.com/2019/12/31/small-changes-big-improvement/

Carlo Alberto Ferraris 提交了一个对math/rand库中的lockedSource优化的 pr(CL#191538[1]),核心代码其实只有一行,却带来了相对的巨大的性能提升,让我们一起老看看这次的修改,学习一下代码的优化技巧,提高我们 Go 语言的底层优化经验。

Carlo 通过避免接口调用允许内联保持在同一个cacheline三种方式提升rngSource的性能:

可以看到,他把lockedSourcestruct 中的src字段从接口类型Source64改变了具体的 struct 指针*rngSource,这样*rngSource的方法Int64Uint64都可以内联到调用者的代码中。

实际测试中第三个保持在同一个cacheline优化并没有起作用,反而采用指针类型性能更好一点点。我实际测试发现也发现这个优化也没有特别明显的优化效果,所以下面的测试中并没有这个优化手段。

下面我们通过一个例子 ? 来比较采用这个方法前后的代码的性能,主要观察接口去掉后的性能提升和内联后的性能提升。

首先定义一个干果DryFruit的接口,它有一些通用的方法,比如名称、价格以及增加数量的方法(Increase), 因为只是做个演示,你不必深究这些方法的意义:

// DryFruit 干果接口.type DryFruit interface { Name() string Price() uint64    Family() string   Distribution() string Increase()}

下面我们定义一个栗子 ? 对象,它实现了干果接口:

// Chestnut 栗子.type Chestnut struct {  name  string  count uint64}

// Name 名称.func (c Chestnut) Name() string {return c.name}

// Price 价格.func (c Chestnut) Price() uint64 {return 10}

// Family Family name.func (c Chestnut) Family() string {return "Fagaceae"}

// Distribution 分布.func (c Chestnut) Distribution() string {return "East Asia"}

// Increase 数量加一func (c *Chestnut) Increase() {  c.count++}

接口和具体的实现都定义好了,我们需要定义一个使用它们的对象:礼物Gift

未优化的礼物定义如下, 定义了一个OriginGift对象,它包含一个排外锁,还包含一个干果接口字段:

// OriginGift 未优化之前的礼物对象.type OriginGift struct {   mu       sync.Mutex   dryFruit DryFruit}

// Access 访问接口对象.func (g *OriginGift) Access() {    g.dryFruit.Name() g.dryFruit.Price()    g.dryFruit.Family()   g.dryFruit.Distribution() g.dryFruit.Increase()}

而我们的优化的 Gift struct 直接把接口对象替换成具体的栗子 struct:

// ImprovedGift 优化后的礼物对象.type ImprovedGift struct { mu       sync.Mutex   dryFruit *Chestnut}

// Access 访问具体的字段对象.func (g *ImprovedGift) Access() {  g.dryFruit.Name() g.dryFruit.Price()    g.dryFruit.Family()   g.dryFruit.Distribution() g.dryFruit.Increase()}

Benchmark 测试代码如下:

func BenchmarkOriginGift(b *testing.B) {var nut = &OriginGift{        dryFruit: &Chestnut{name: "栗子"},    }for i := 0; i < b.N; i++ {       nut.Access()  }}

func BenchmarkImprovedGift(b *testing.B) {var nut = &ImprovedGift{     dryFruit: &Chestnut{name: "栗子"},    }for i := 0; i < b.N; i++ {       nut.Access()  }}

func BenchmarkOriginGiftParallel(b *testing.B) {var nut = &OriginGift{     dryFruit: &Chestnut{name: "栗子"},    } b.RunParallel(func(pb *testing.PB) {for pb.Next() {         nut.mu.Lock()         nut.Access()          nut.mu.Unlock()       } })}

func BenchmarkImprovedGiftParallel(b *testing.B) {var nut = &ImprovedGift{        dryFruit: &Chestnut{name: "栗子"},    } b.RunParallel(func(pb *testing.PB) {for pb.Next() {         nut.mu.Lock()         nut.Access()          nut.mu.Unlock()       } })}

同时测试无并发的 benchmark, 然后测试并发访问时的性能。

第一次测试我们采用禁止内联的方式, 运行go test -gcflags "-N -l" -bench .:

goos: darwingoarch: amd64pkg: github.com/smallnest/study/perf_interfaceBenchmarkOriginGift-4                34669898            31.0 ns/opBenchmarkImprovedGift-4             58661895            17.9 ns/opBenchmarkOriginGiftParallel-4        7292043           171 ns/opBenchmarkImprovedGiftParallel-4        8718816           143 ns/op

可以看到将接口替换成具体的 struct 优化还是很明显的,非并发访问的情况下耗时几乎降到了一半,并发访问也有可观的性能提升。

第二次我们启用内联,看看和上面不启用内联的情况比较。

goarch: amd64pkg: github.com/smallnest/study/perf_interfaceBenchmarkOriginGift-4              95278143            12.6 ns/opBenchmarkImprovedGift-4             549471100            2.16 ns/opBenchmarkOriginGiftParallel-4      11631438           115 ns/opBenchmarkImprovedGiftParallel-4       13815229            86.3 ns/op

启用内联后,可以看到性能都有所提升,而去掉接口的方式性能优化更明显,直接降到了2.16 ns/op

通过这两个 benchmark 的比较,你应该能深刻理解到这两种优化手段(去接口、内联)带来的巨大收益吧。

你可以通过 go test --gcflags "-m -m" -bench . 了解内联的具体细节。

参考资料

[1]

CL#191538: https://go-review.googlesource.com/c/go/+/191538/

推荐阅读

  • xxx


喜欢本文的朋友,欢迎关注“Go语言中文网”:

Go语言中文网启用微信学习交流群,欢迎加微信:274768166

go string 转 uint64_小改动,大提升:最近 Go 标准库的一次优化相关推荐

  1. 谈谈两种标准库类型---string和vector

    两种最重要的标准库---string和vector string和vector是两种最重要的标准库类型,string表示可变长的字符序列,vector存放的是某种给定类型对象的可变长序列. 一.标准库 ...

  2. 从零开始学C++之标准库类型(一):string 类简介和例程

    一.标准库string类型 string类型支持长度可变的字符串,C++标准库将负责管理与存储字符相关的内存,以及提供各种有用的操作 ,在VC中直接F1查看 template <class Ch ...

  3. java怎么让多个方法循环运行下去_35 个小细节,提升 Java 代码的运行效率!你知道几个?...

    原标题:35 个小细节,提升 Java 代码的运行效率!你知道几个? 前言 代码优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个 ...

  4. 走进科学-小菌株大作为—枯草芽孢杆菌替代畜牧业抗生素添加

    我国是世界上最大的抗生素生产和使用国,同时也是细菌耐药性的重灾区.抗生素滥用问题在医疗特别是畜牧业领域尤其严重.畜牧业养殖户普遍将饲用抗生素添加到饲料中,当做保健品给动物防病,促进动物生长.而这会导致 ...

  5. 央视与遗传发育所合拍《走进科学-小菌株大作为》——枯草芽孢杆菌替代畜牧业抗生素添加

    我国是世界上最大的抗生素生产和使用国,同时也是细菌耐药性的重灾区.抗生素滥用问题在医疗特别是畜牧业领域尤其严重.畜牧业养殖户普遍将饲用抗生素添加到饲料中,当做保健品给动物防病,促进动物生长.而这会导致 ...

  6. 小块头大性能才能得到用户的青睐

    小块头大性能才能得到用户的青睐<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" ...

  7. 高密自智,体小量大,希捷Exos Corvault存储系统为数据洞察赋能

    2022年2月24日--全球领先的海量数据存储基础设施解决方案提供商希捷科技(NASDAQ:STX)在线上举办了主题为"高密自智,体小量大"的新一代PB级自修复存储系统--Exos ...

  8. 中台彻底搞砸了?下一站,小中台大前台

    作者| Mr.K   责编| 丁恩华 上周,老 K 受邀参加了一场大型线下技术峰会,作了中台方面的主题演讲,现场反馈热烈,微信加到爆.演讲的内容,总结了 2020 年中台在行业的实践,以及老 K 对中 ...

  9. 拨号盘拨号数字间距太小 调大 修改通讯录里面收藏和所有联系人字体颜色

    Z:\80_l_hct_third\update\tengtai\v138f_qhd_tengtai_t36_b1b5_4Gb\packages\apps\PhoneCommon\res\values ...

最新文章

  1. JS设计模式(13)状态模式
  2. 蚂蚁金服面试3+2次,最终有惊无险通过!
  3. Xshell高级后门完整分析报告
  4. r 保留之前曲线_R简单数据处理和分析
  5. Common tasks for MySQL
  6. assert函数_PHP 之 assert()函数
  7. 【离散数学】浅析小项与主析取范式和大项与主合取范式
  8. 信息学奥赛一本通 1092:求出e的值 | OpenJudge NOI 1.5 35
  9. 作用于HTML元素的Vue.js指令
  10. 基于情感词典的情感值分析
  11. 「leetcode」617. 合并二叉树:【三种递归】【一种迭代】详解
  12. pp助手苹果版_PP助手将下架?越狱时代已经过去,到底是该喜该悲
  13. 放大镜css图标,CSS放大镜的制作
  14. [英语语法]句法之句子成分和种类
  15. 星际迷航中企业号的动力系统_五月四日与您同在:《星球大战》(和《星际迷航》)如何激发现实生活中的技术...
  16. C#学习笔记:GDI图形高级编程(1)——如何将图形画到控件上
  17. 第十二周项目四----利用遍历思想求解图问题之7最远顶点
  18. 【CodeForces300D】Painting Square
  19. 老有“美女”加你微信?大学生“艳遇”,结果吓一跳...
  20. Kotlin使用高阶函数实现多方法回调

热门文章

  1. C库函数-perror()
  2. 怎么html跟asp结合,xml与asp简单结合实现html模板功能.doc
  3. envoy实现_微服务之服务治理:Envoy 全局 gRPC 限速服务 lyft/ratelimit 详解
  4. matlab 傅里叶变换_Matlab与傅里叶变换
  5. TransDecoder
  6. 农林废弃物如何变废为宝?
  7. CHM:植物利用细菌获得真菌抗性!中山大学李剑峰课题组揭示植物免疫预警新机制...
  8. 不需要懂得编程,但却可以使用ggplot2画出论文级别的图?
  9. R语言可视化散点图(scatter plot)、并在散点图中叠加回归曲线、叠加lowess拟合曲线(linear and lowess fit lines)、使用plot、line、abline函数
  10. pandas使用groupby函数按照多个分组变量进行分组聚合统计、使用agg函数计算分组的多个统计指标(grouping by multiple columns in dataframe)