Julia:抽象类型与类型联合

Posted

技术标签:

【中文标题】Julia:抽象类型与类型联合【英文标题】:Julia: Abstract types vs Union of types 【发布时间】:2022-01-09 16:16:46 【问题描述】:

我对 Julia 很陌生,在尝试做某些事情时,我仍然对哪种风格更好有一些疑问......例如,我对使用抽象类型与定义的性能或风格差异有很多疑问工会。

一个例子:假设我们想要实现几种类型的单元(Mob、Knight、...),它们应该共享它们的大部分(如果不是全部)属性和大部分(如果不是全部)方法。 我看到提供结构的两个选项:首先,可以声明一个抽象类型AbstractUnit,其他类型从该抽象类型派生,然后为抽象类型创建方法。它看起来像这样:

abstract type AbstractUnit end

mutable struct Knight <: AbstractUnit
    id     :: Int
    [...]
end

mutable struct Peasant <: AbstractUnit
    id     :: Int
    [...]
end

id(u::T)      where T <: AbstractUnit = u.id
[...]

或者,可以定义类型的联合并为联合创建方法。它看起来像这样:

mutable struct Knight
    id     :: Int
    [...]
end

mutable struct Peasant
    id     :: Int
    [...]
end

const Unit = UnionKnight,Peasant

id(u::Unit) = u.id
[...]

我了解这两种方法之间的一些概念差异,并认为第一种方法更具可扩展性。但是,我对性能有很多疑问。例如,在运行时的内存分配方面,创建AbstractUnit 的数组与联合类型的数组会有多糟糕?

谢谢!

【问题讨论】:

【参考方案1】:

在方法的参数类型注释中绝对使用AbstractUnit 而不是Unit。实际上,联合也是抽象类型,但正如您所指出的,您不能向其中添加新类型。在任何一种情况下,该方法都会被编译为每个具体类型(如 KnightPeasant)的特化,因此您的方法的性能不会有所不同。

至于数组的元素类型参数,有isbits Union optimization,但顾名思义,它只有在你的联合中的所有类型都是isbitstypes 时才有效(没有指针,不可变)。您的结构是可变的,因此这已经不适用。看,当实例直接存储在数组中时,内存访问速度更快,并且元素类型参数(VectorT 中的T)必须是具体的isbitstype 才能允许这样做。当元素类型参数是抽象的或可变的时,一般数组只直接存储指向实际实例的指针,因为多个或可变的具体类型可能具有未知且可变的内存大小。如果抽象类型是isbits Union,实例可以直接存储在 Array 中:为每个元素分配足够的内存以包含 Union 中最大的具体类型以及指定其类型的每个元素的标记字节。一个字节只有 256 个值,所以大概这仅适用于最多 256 个具体类型的联合。

VectorAbstractUnit 上使用VectorUnit 的另一个可能优化是Union-splitting 类型不稳定性。我真的无法制作一个比链接的博客更好地解释它的示例方法,所以我只给出简短的版本。当 Julia 的编译器根本无法推断出方法中变量的类型时(@code_warntype 中的 ::Any 注释),涉及变量的内部方法调用必须在运行时进行类型检查和分派(选择特化),这可能会花费大量时间。但是,如果 Julia 的编译器可以将变量推断为几个具体类型的联合(实际上最多 4 个),则可以使用每种类型的条件分支来消除大多数类型检查并在编译时进行分派。 VectorAbstractUnit 可以包含任意数量的类型&lt;: AbstractUnit,因此编译器不能使用联合拆分。 VectorUnit 但是让编译器知道元素必须是 KnightPeasant,这允许联合拆分。

附:对于初学者来说,这通常是一个混淆的来源,但是Unit 是一个抽象类型,VectorUnit 是一个具体类型,只是带有一个抽象类型参数。毕竟,它可以有实例,所有的数组都直接包含指向KnightPeasant 实例的指针。

【讨论】:

感谢您的回答!因此,如果我正确掌握所有内容,我可能应该同时定义 Abstract 类型和 Union 类型。然后我将使用抽象类型实现方法,但使用联合类型分配巨大的数组(希望联合拆分)。对吗? 如果您可以将 Array 的元素限制为最多 3-4 种具体类型,那么可以进行优化。你不需要提前为你的联合定义变量,实际上,你可以写unit_array = UnionKnight, Peasant[]。然而在实践中,很容易需要处理比这更多的类型,在这种情况下,您必须要么降低性能,要么重写多个数组,每个数组只包含 1 个具体类型。 好的,很好。非常感谢您的回答!

以上是关于Julia:抽象类型与类型联合的主要内容,如果未能解决你的问题,请参考以下文章

优化中的 Julia 抽象类型?

从抽象类型访问字段时,julia 类型不稳定

Kotlin学习笔记——接口抽象类泛型扩展集合操作符与Java互操作性单例

冒号课堂阅读笔记OO的多态抽象类型——实中之虚

Java抽象类与接口的区别:

PHP 类型提示与接口和抽象类不相处?