Dynamic dispatch
Dynamic dispatch
动态调度。动态分发
In computer science, dynamic dispatch is the process of selecting which implementation of a polymorphic operation (method or function) to call at run time. It is commonly employed in, and considered a prime characteristic of, object-oriented programming (OOP) languages and systems.[1]
在计算机科学中,动态调度是“选择一个多态操作(方法或功能)的实现,以在运行时确定调用哪个”的过程。它是通常使用的在面向对象的编程(OOP)语言和系统的一个主要特征。[1]
Object-oriented systems model a problem as a set of interacting objects that enact operations referred to by name. Polymorphism is the phenomenon wherein somewhat interchangeable objects each expose an operation of the same name but possibly differing in behavior. As an example, a File object and a Database object both have a StoreRecord method that can be used to write a personnel record to storage. Their implementations differ. A program holds a reference to an object which may be either a File object or a Database object. Which it is may have been determined by a run-time setting, and at this stage, the program may not know or care which. When the program calls StoreRecord on the object, something needs to decide which behavior gets enacted. If one thinks of OOP as sending messages to objects, then in this example the program sends a StoreRecord message to an object of unknown type, leaving it to the run-time support system to dispatch the message to the right object. The object enacts whichever behavior it implements.[2]
面向对象的系统把问题建模为“一组相互作用的对象”,互相通过名称沟通。多态性是指对象有公开名称相同的操作,但在行为上可能有所不同。比如,一个文件对象和数据库对象都有一个StoreRecord方法可以用来编写人员记录到存储。他们的实现有所不同。。。。
Dynamic dispatch contrasts with static dispatch, in which the implementation of a polymorphic operation is selected at compile-time. The purpose of dynamic dispatch is to support cases where the appropriate implementation of a polymorphic operation cannot be determined at compile time because it depends on the runtime type of one or more actual parameters to the operation.
与dd相对的是sd静态调度,多态操作的选择是在编译时确定
Dynamic dispatch is different from late binding (also known as dynamic binding). In the context of selecting an operation, binding associates a name to an operation. Dispatching chooses an implementation for the operation after you have decided which operation a name refers to. With dynamic dispatch, the name may be bound to a polymorphic operation at compile time, but the implementation not be chosen until run time. While dynamic dispatch does not imply late binding, late binding does imply dynamic dispatching since the binding is what determines the set of available dispatches.
动态调度和迟绑定(动态绑定)不同。在选择操作方面,绑定把名称和操作联系到一起,而Dispatching为一个操作选择一个实现。对于dd,名称可能在编译时确定,而实现实在运行时确定
Single and multiple dispatch[edit]
单分发和多分发
Main article: Multiple dispatch
The decision of which version of a method to call may be based either on a single object, or on a combination of objects. The former is called single dispatch and is directly supported by common object-oriented languages such as C++, Java, Smalltalk, Objective-C, Swift, JavaScript, and Python. In these and similar languages, one may call a method for division with syntax that resembles
确定哪个方法可能是基于单个对象,或者多个对象的组合。单分发一般oo语言都支持,比如c++,java,smalltalk,oc,...
举例:dividend.divide(divisor)
where the parameters are optional. This is thought of as sending a message named divide with parameter divisor to dividend. An implementation will be chosen based only on dividend's type (perhaps rational, floating point, matrix), disregarding the type or value of divisor.
By contrast, some languages (such as Common Lisp, Dylan, Julia) dispatch methods or functions based on the combination of operands; in the division case, the types of the dividend and divisor together determine which divide operation will be performed. This is known as multiple dispatch.
一些语言分发消息基于操作的组合:这叫做多分发
Dynamic dispatch mechanisms[edit]
动态分发机制
A language may be implemented with different dynamic dispatch mechanisms. The choices of the dynamic dispatch mechanism offered by a language to a large extent alter the programming paradigms that are available or are most natural to use within a given language.
Normally, in a typed language, the dispatch mechanism will be performed based on the type of the arguments (most commonly based on the type of the receiver of a message). This might be dubbed 'per-type dynamic dispatch'. Languages with weak or no typing systems often carry a dispatch table as part of the object data for each object. This allows instance behaviour as each instance may map a given message to a separate method.
Some languages offer a hybrid approach.
Dynamic dispatch will always incur an overhead so some languages offer static dispatch for particular methods.
C++ implementation[edit]
cpp实现
C++ uses early binding and offers both dynamic and static dispatch. The default form of dispatch is static. To get dynamic dispatch the programmer must declare a method as virtual.
cpp同时支持动态和静态绑定。动态绑定方法需要用virtual声明
C++ compilers typically implement dynamic dispatch with a data structure called a virtual table (vtable) that defines the message-to-method mapping for a given class (C++ as such has no notion of a vtable). Instances of that type will then store a pointer to this table as part of their instance data. This is complicated when multiple inheritance is used. Since C++ does not support late binding, the virtual table in a C++ object cannot be modified at run-time, which limits the potential set of dispatch targets to a finite set chosen at compile time.
cpp编译器通过vtable实现动态分发,vtable定义了指定class的message-to-method mapping。对象会保存vtablke的指针,当存在多继承的时候会很复杂。由于cpp不支持迟绑定,vtable不可以运行时修改,这限制了分发对象的数目在编译期间
Type overloading does not produce dynamic dispatch in C++ as the language considers the types of the message parameters part of the formal message name. This means that the message name the programmer sees is not the formal name used for binding.
Go and Rust implementation[edit]
go的实现
In Go and Rust, a more versatile variation of early binding is used. Vtable pointers are carried with object references as 'fat pointers' ('interfaces' in go, or 'trait objects' in Rust).
go使用了早期绑定的一个变体。vtable指针被object 指向,扎叫做'fat pointers' (是说不是每个type一个vtable,而是每个object?)
This decouples the supported interfaces from the underlying data structures. Each compiled library needn't know the full range of interfaces supported in order to correctly use a type, just the specific vtable layout that they require. Code can pass around different interfaces to the same piece of data to different functions. This versatility comes at the expense of extra data with each object reference, which is problematic if many such references are stored persistently.
这解耦了interfacre和底层的data structures.每个lib不需要知道所有支持的interface以便正确的使用该类型
Smalltalk implementation[edit]
Smalltalk的实现
Smalltalk uses a type-based message dispatcher. Each instance has a single type whose definition contains the methods. When an instance receives a message, the dispatcher looks up the corresponding method in the message-to-method map for the type and then invokes the method.
st使用了 type-based的消息分发器。每个实例收到消息后,dispatcher查找对性的方法,再invoke该方法
Because a type can have a chain of base types, this look-up can be expensive. A naive implementation of Smalltalk's mechanism would seem to have a significantly higher overhead than that of C++ and this overhead would be incurred for each and every message that an object receives.
由于type会有base type链表,这个查找会很慢
Real Smalltalk implementations often use a technique known as inline caching[3] that makes method dispatch very fast. Inline caching basically stores the previous destination method address and object class of the call site (or multiple pairs for multi-way caching). The cached method is initialized with the most common target method (or just the cache miss handler), based on the method selector. When the method call site is reached during execution, it just calls the address in the cache. (In a dynamic code generator, this call is a direct call as the direct address is back patched by cache miss logic.) Prologue code in the called method then compares the cached class with the actual object class, and if they don't match, execution branches to a cache miss handler to find the correct method in the class. A fast implementation may have multiple cache entries and it often only takes a couple of instructions to get execution to the correct method on an initial cache miss. The common case will be a cached class match, and execution will just continue in the method.
真正的实现会使用inline caching加速消息分发。???
Out-of-line caching can also be used in the method invocation logic, using the object class and method selector. In one design, the class and method selector are hashed, and used as an index into a method dispatch cache table.
As Smalltalk is a reflective language, many implementations allow mutating individual objects into objects with dynamically generated method lookup tables. This allows altering object behavior on a per object basis. A whole category of languages known as prototype based languages has grown from this, the most famous of which are Self and JavaScript. Careful design of the method dispatch caching allows even prototype based languages to have high performance method dispatch.
由于Smalltalk是反射语言,很多实现对象使用动态生成的method lookup tables。这使得修改objct的香味成为可能。一种基于prototype的语言由此而来。最常见的就是 Self and JavaScript.仔细的设计method dispatch caching可以使prototype based languages都会有好的分发性能
Many other dynamically typed languages, including Python, Ruby, Objective-C and Groovy use similar approaches.
其他动态语言比如python,。。使用类似的技术