Smalltalk动态查找优化
在Smalltalk中,在运行时查找方法可能涉及大量步骤,因为子类的方法字典在其超类中不包含方法,并且需要一个指针追踪来查找该方法。每个子类的优化是将所有超类方法存储在其方法字典中。Smalltalk动态查找优化,smalltalk,dynamic-dispatch,Smalltalk,Dynamic Dispatch,在Smalltalk中,在运行时查找方法可能涉及大量步骤,因为子类的方法字典在其超类中不包含方法,并且需要一个指针追踪来查找该方法。每个子类的优化是将所有超类方法存储在其方法字典中。 问题:如何做到这一点 一个明显的缺点是空间成本,但我只是想知道如何在Smalltalk中做到这一点?这与为最近调用的方法创建单独的缓存不同 在Smalltalk中,在运行时查找方法可能涉及大量步骤,因为子类的方法字典在其超类中不包含方法 这对于不尝试进行任何优化的解释型Smalltalk来说是正确的,许多Small
问题:如何做到这一点 一个明显的缺点是空间成本,但我只是想知道如何在Smalltalk中做到这一点?这与为最近调用的方法创建单独的缓存不同 在Smalltalk中,在运行时查找方法可能涉及大量步骤,因为子类的方法字典在其超类中不包含方法 这对于不尝试进行任何优化的解释型Smalltalk来说是正确的,许多Smalltalk(例如Pharo)实际上都是编译的,并且虚拟机中有很多优化……因此消息查找肯定不是我担心的性能问题 至于所谓的类扁平化…这种简单的方法实际上非常简单,只需复制所有的父方法
Child withAllSuperclassesDo: [ :cls |
cls methods do: [ :m |
(Child selectors includes: m selector) ifFalse: [
Child compile: m sourceCode classified: m protocol
]
]
]
毫无疑问,所有的方法都存在
您可能不想复制完整的层次结构,但可以轻松地将其限制为一个包或几个祖先
超级怎么样?
super
可能有一个不太好的捕获,它依赖于在层次结构中的特定位置。例如,如果您重写父级中使用super
…的某个方法并调用该方法,然后将其展平…您可能会陷入无休止的循环,甚至更糟
因此,如果所有这些引用都是正确的,您可能需要分析源代码……我不确定这是否可以自动完成,因为这可能取决于代码的逻辑,您必须用自己的眼睛和头脑来分析。这也意味着您不能盲目复制所有方法或从多个类中指向同一方法,因为super
具有不同的含义
正如阿莫斯恰当地指出的那样……这是无法解释的
更新以回答@aka.nice的评论(并进一步说明super
有问题的原因)
压平前在左边,压平后在右边
几乎在任何时候,包含super的方法都会做一些事情(正如它应该做的那样),您基本上会调用该行为至少两次。一些方言使用另一种技术来最小化枚举
方法字典链所需的操作数:所谓的MDA
或方法字典数组
其思想是让类在第一个插槽中保留一个数组,其中包含其实例的MD
,然后是其超类的MD
,依此类推
这种技术的一个优点是它可以在实例级别实现。可以存储类的MDA
,而不是将类存储在对象头中。这与方法发送中发生的类检查是100%兼容的,只是它变成了MDA
-检查
此外,如果将MDA
具体化,则可以向其添加行为,使其支持嵌套。这样,外部阵列可以包含MDs
或嵌套MDA
。嵌套可用于添加特定于实例的行为,而无需脱离类的MDA
:将特定于实例的行为放在MD
中,并将其存储在插槽1中,然后将类的MDA
放在插槽2中。如果类的MDA
发生更改,则实例将不必执行任何特殊操作来附加到修改后的MDA
。(如果实例创建了自己的MDA
,其MD
位于插槽1中,而类MDs
位于后续插槽中,则类的MDA
将与实例分离)
顺便说一句,空间成本很小,因为没有重复的内容。缓存查找结果的方法很多。只需阅读一些基本的VMS实施文件——从绿皮书开始。你甚至可以在网上免费找到t,并在底部附近寻找“一些历史,一些建议”。
普通解释器可以使用简单的哈希键缓存。转换虚拟机可以进入内联缓存、多态内联缓存、自适应缓存。。。不需要任何东西像C++ VTHES一样痛苦,这正是你所建议的。
几十年来我们一直在做这件事。问题解决了 要指出的是,空间成本并不是唯一的缺点。想象一下,你想重构一个类,使之拥有一个不同的超类。。。哎哟。另外,您不仅需要存储超类方法,还需要存储它们在层次结构中的位置,这样您就可以正确地确定哪一个与调用类“最接近”。在我看来,你提到的“大量步骤”并没有那么低效,因此需要重新考虑。你在寻找什么好处?我的+1只是为了使用“不可分解”;-)对于super,如果只从方法a
调用super a
将很容易:那么我们只需将方法导入而不发送给super。。。但并非所有方法都表现良好,我们有时会从方法b
中看到super a
,这会禁止导入发送到super的方法,而不仅仅是发送到super的方法。。。仍然可行。@Peter,但您不会精确地导入a,因为某些超类发送超级a。这与我并不矛盾。。。