Smalltalk动态查找优化

Smalltalk动态查找优化,smalltalk,dynamic-dispatch,Smalltalk,Dynamic Dispatch,在Smalltalk中,在运行时查找方法可能涉及大量步骤,因为子类的方法字典在其超类中不包含方法,并且需要一个指针追踪来查找该方法。每个子类的优化是将所有超类方法存储在其方法字典中。 问题:如何做到这一点 一个明显的缺点是空间成本,但我只是想知道如何在Smalltalk中做到这一点?这与为最近调用的方法创建单独的缓存不同 在Smalltalk中,在运行时查找方法可能涉及大量步骤,因为子类的方法字典在其超类中不包含方法 这对于不尝试进行任何优化的解释型Smalltalk来说是正确的,许多Small

在Smalltalk中,在运行时查找方法可能涉及大量步骤,因为子类的方法字典在其超类中不包含方法,并且需要一个指针追踪来查找该方法。每个子类的优化是将所有超类方法存储在其方法字典中。
问题:如何做到这一点

一个明显的缺点是空间成本,但我只是想知道如何在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。这与我并不矛盾。。。