Julia中的运行时多态性开销



我知道Julia在很大程度上依赖实时静态类型派生(基本上所有代码都需要被视为c++模板)。我还了解到,这意味着在不同类型的对象上使用单一算法时,只要这些类型在编译时是已知的,就不会有运行时开销。

当谈到运行时多态性时,我不太清楚它是如何工作的。假设我们有以下情况:

abstract Shape
type Circle <: Shape
    radius::Float64
end
type Square <: Shape
    width::Float64
end
dist(x::Circle, y::Circle) = ...
dist(x::Circle, y::Square) = ...
dist(x::Square, y::Circle) = ...
dist(x::Square, y::Square) = ...

s = get_shape()
t = get_shape()
a = dist(s,t)

这里,get_shape可以基于例如用户输入返回圆形或正方形。在c++中,调度只需要进行一次虚拟表查找。朱莉娅是怎么做到的?多重调度背后的机制是什么?它是否比虚拟表查找贵得多?从同一抽象类型派生SquareCircle有什么好处吗?或者这在运行时调度的上下文中完全无关吗?

EDT:在本例中运行@code_warntype会给出:

Variables:
  s::Union{Circle,Square}
  t::Union{Circle,Square}
Body:
  begin  # none, line 2:
      s = (Main.get_shape)()::Union{Circle,Square} # none, line 3:
      t = (Main.get_shape)()::Union{Circle,Square} # none, line 4:
      return (Main.dist)(s::Union{Circle,Square},t::Union{Circle,Square})::ASCIIString
  end::ASCIIString

因此,编译器并非完全不知道s和t的类型。在调用dist时,这些知识是否用于加快调度?

当同一函数有多个方法时,julia通过类型交集使用方法查找(将参数的类型与签名中的类型匹配)来确定调用哪个方法。如果可以推断类型,则可以在编译代码时执行该计算。通过提前进行查找,它不必在运行时执行类型交集,这样可以获得最佳性能。

当类型不可预测时,julia必须在运行时确定要调度到哪个方法。如果被调用的函数正在做少量的工作,这有时会成为运行时的瓶颈。(当它做了很多工作时,查找对性能基本上无关紧要)。

对于julia来说,这是一个比OOP语言稍微复杂一些的问题,因为正确的方法取决于所有的参数,而不仅仅是第一个参数。

我知道这是旧的,但如果有人对数字感兴趣,我会找到一个小基准。事实证明,目前(1.1)使用抽象类型的运行时多态性非常慢(30倍于静态调度,6倍于C++虚拟函数调用)。

最新更新