【发布时间】:2022-01-02 20:59:25
【问题描述】:
当类型推断失败时(@code_warntype 打印输出中的::Any),我的理解是函数调用是动态调度的。换句话说,在运行时,会检查参数的类型以找到具体参数类型的特化 (MethodInstance)。需要在运行时而不是编译时执行此操作会导致性能成本。
(编辑:本来,我在类型检查和专业化查找之间说“多次调度找到合适的方法”,但我实际上不知道这部分是否发生在运行时。似乎只需要发生如果不存在有效的特化并且需要编译。)
在只需要检查一个参数的具体类型的情况下,是否可以进行更快的动态单次调度,例如在某种专业化查找表中?我只是找不到访问和调用MethodInstances 的方法,就好像它们是函数一样。
当谈到改变调度或专业化时,我想到了invoke 和@nospecialize。 invoke 看起来可能会直接跳到指定的方法,但仍然必须检查多个参数类型和特化。 @nospecialize 不会跳过调度过程的任何部分,只会产生不同的特化。
编辑:一个带有 cmets 的最小示例,希望能描述我在说什么。
struct Foo end
struct Bar end
# want to dispatch only on 1st argument
# still want to specialize on 2nd argument
baz(::Foo, ::Integer) = 1
baz(::Foo, ::AbstractFloat) = 1.0
baz(::Bar, ::Integer) = 1im
baz(::Bar, ::AbstractFloat) = 1.0im
x = Any[Foo(), Bar(), Foo()]
# run test1(x, 1) or test1(x, 1.0)
function test1(x, second)
# first::Any in @code_warntype printout
for first in x
# first::Any requires dynamic dispatch of baz
println(baz(first, second))
# Is it possible to only dispatch -baz- on -first- given
# the concrete types of the other arguments -second-?
end
end
【问题讨论】:
-
一种方法是将其余参数放入关键字参数中,因为这些参数不参与 MD,但也许举个例子会有所帮助
-
我会写一个例子来运行,但我无法演示这么多的内部工作原理。
-
可能不是您要查找的内容,但您可以使用明确的 if 分支根据第一个参数“调度”作为解决方法。这会很快。
-
@carstenbauer 1) 是的,在我的示例中检查
first的具体类型的冗余 if-elseif 导致分支中的静态调度。尚未检查这是否可以扩展到更多类型或达到联合拆分限制之类的东西。 2) ManualDispatch.jl 似乎简化了 if-elseif 检查并且必须检查所有类型。检查线性 if-elseif 中的多种类型似乎没有最佳扩展,但分支静态调度可能是值得的。 3)Unityper.jl很酷,但是它把类型合并为1,所以我好像不能写一个多方法,只是每个分支的功能不同。
标签: dynamic julia single-dispatch