Julia中抽象类型与类型联合的性能及编码风格差异咨询
抽象类型与联合类型的性能、规范差异解答
性能差异
数组存储与访问性能
- 联合类型数组(小联合场景)
Julia 1.8+ 对成员数量≤4、内存布局一致的小联合做了专门优化:你定义的Union{Knight,Peasant}属于典型小联合,Vector{Unit}会直接分配连续的inline内存块,和单类型数组的存储布局完全一致,不需要指针跳转,访问、遍历性能和单类型数组几乎没有差异,创建时的内存分配开销也和同大小单类型数组一致。 - 抽象类型数组
只要元素类型声明为AbstractUnit这类抽象类型,不管子类数量多少、内存布局是否一致,Vector{AbstractUnit}都会存储为指针数组:每个元素是指向堆上对象的64位指针,访问时需要先解引用指针,再做运行时类型分发,遍历性能比小联合数组低3~10倍不等,数组越大、访问越频繁差距越明显。
方法分发性能
- 小联合类型的参数传入函数时,编译器会自动做联合拆分优化,为每个联合成员生成独立的优化代码分支,分发性能和具体类型参数几乎一致。
- 抽象类型参数如果编译器无法推导具体类型,会走动态分发,每次调用都需要运行时查找方法表,性能明显低于小联合的静态分发。
编码规范与适用场景
两种方案没有绝对的优劣,需要根据业务场景选择:
- 优先选择抽象类型的场景:
- 后续需要大量扩展子类,比如你后续要新增Mob、Archer、Mage等几十种单位类型,抽象类型只需要新子类继承即可,不需要修改原有代码;而联合类型需要不断修改
Unit的定义,维护成本极高。 - 需要构建类型层级,比如后续要细分
AbstractHuman <: AbstractUnit、AbstractMonster <: AbstractUnit等子抽象类型做分层接口设计,联合类型完全不支持类型层级的表达。 - 对外提供公共接口,允许第三方扩展适配的场景,其他开发者只要继承你的抽象类型就能直接适配所有通用方法,不需要修改你定义的联合类型。
- 后续需要大量扩展子类,比如你后续要新增Mob、Archer、Mage等几十种单位类型,抽象类型只需要新子类继承即可,不需要修改原有代码;而联合类型需要不断修改
- 优先选择联合类型的场景:
- 类型数量完全固定,长期不会新增成员,比如你确定单位永远只有Knight、Peasant两种。
- 对性能要求极高,比如游戏场景下需要每秒遍历上万单位做逻辑更新,小联合数组的性能优势非常明显。
- 多个类型只是刚好有共同接口,逻辑上不属于同一类概念,不需要建立类型层级的场景。
针对你的场景的建议
你做的游戏单位场景大概率需要后续扩展新类型,更推荐使用抽象类型的方案。如果确实有性能需求,可以考虑另一种折衷方案:用单个结构体加枚举标记区分单位类型,代码示例如下:
@enum UnitType Knight Peasant Mob mutable struct Unit type::UnitType id::Int # 其他公共属性 end
这种方案的数组也是连续inline存储,性能和小联合数组一致,扩展新类型只需要加枚举成员即可,兼顾可扩展性和性能。
内容的提问来源于stack exchange,提问作者Jordi Manyer Fuertes
相关产品推荐
相关产品推荐

