Golang Go语言中 Dig101-Go 的 interface 调用优化点

发布于 1周前 作者 nodeper 来自 Go语言

Golang Go语言中 Dig101-Go 的 interface 调用优化点

Dig101: dig more, simplified more and know more

今天谈下上文( Dig101-Go 之读懂 interface 的底层设计 )留下的那个问题:

为什么对于以下 interface Stringer 和构造类型 Binary

下面代码conversion会调用转换函数convT64,而devirt不会调用?

func conversion() {
  var b Stringer
  var i Binary = 1
  b = i //convT64
  _=b.String()
}

func devirt() { var b Stringer = Binary(1) _ = b.String() //static call Binary.String }

这里可以使用 ssa 可视化工具查看,更容易了解每行代码的编译过程 如 GOSSAFUNC=main go1.14 build types/interface/interface.go 生成ssa.html ssa.html

事有蹊跷,必是优化!

搜索发现相关 issue Devirtualize calls when concrete type behind interface is statically known 和提交 De-virtualize interface calls

原来这个是为了优化如果 interface 内部的构造类型如果可以内联后被静态推断出来的话,就将其直接重写为静态调用

最初主要希望避免一些 interface 调用的 gc 压力( interface 调用在逃逸分析时,会使函数的接受者(receiver)和参数(argument)逃逸到堆上(而不是留在栈上),增加 gc 压力。不过这一点目前还未实现,参见Use devirtualization in escape analysis

暂时先优化为静态调用避免转换调用(convXXX),减少代码大小和提升细微的性能

摘录主要处理点如下:

// 对 iface=类指针( pointer-shaped )构造类型 记录 itab
// 用于后续优化掉 OCONVIFACE
cmd/compile/internal/gc/subr.go:implements
  if isdirectiface(t0) && !iface.IsEmptyInterface() {
    itabname(t0, iface)
  }
cmd/compile/internal/gc/reflect.go:itabname
  itabs = append(itabs, itabEntry{t: t, itype: itype, lsym: s.Linksym()})
// 编译前,获取 itabs
cmd/compile/internal/gc/reflect.go:peekitabs
// ssa 时利用函数内联和 itabs 推断可重写为静态调用,避免 convXXX
cmd/compile/internal/ssa/rewrite.go:devirt

Go 编译步骤相关参见 Go compiler

这种优化对于常见的返回 interface 的构造函数还是有帮助的。

func New() Interface { return &impl{...} }

要注意返回构造类型需为类指针才可以。

我们可以利用这一点来应用此 interface 调用优化

想了解更多,可以查看Devirtualize 的测试代码

本文代码见 NewbMiao/Dig101-Go


欢迎关注公众号:newbmiao,获取及时更新文章。

推荐阅读:Dig101-Go 系列,挖一挖技术背后的故事。


更多关于Golang Go语言中 Dig101-Go 的 interface 调用优化点的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang Go语言中 Dig101-Go 的 interface 调用优化点的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang(Go语言)中,interface 是实现多态性和抽象的关键机制。对于 Dig101-Go 或任何基于Go的项目,优化 interface 调用通常涉及减少运行时反射开销和提高类型断言的效率。以下是一些优化 interface 调用的建议:

  1. 减少不必要的 interface 使用:尽量在明确类型间调用方法,避免不必要的 interface 转换。如果函数签名可以明确类型,就不要使用 interface{}

  2. 使用类型断言优化:在需要的地方,使用类型断言将 interface{} 转换为具体类型,以减少运行时类型检查的开销。这通常适用于需要频繁访问 interface 内部具体类型字段或方法的场景。

  3. 类型参数(Generics):从Go 1.18开始,Go引入了类型参数(Generics),这提供了一种在编译时保持类型安全的方式,避免了 interface 带来的部分运行时开销。考虑使用泛型来代替某些 interface 的使用。

  4. 代码重构:有时候,通过重构代码,将使用 interface 的逻辑移到更少的函数调用点,可以减少运行时开销。例如,可以通过批量处理来减少类型断言的次数。

  5. 性能分析:使用Go的 pprof 工具进行性能分析,找出 interface 调用中的瓶颈,针对性地进行优化。

总之,优化 interface 调用需要具体问题具体分析,结合项目实际情况,采取合适的策略来减少运行时开销,提高代码性能。

回到顶部