Interface Dispatch (2018)(lukasatkinson.de) |
Interface Dispatch (2018)(lukasatkinson.de) |
Delphi implemented interfaces like the C++ approach, and in order to be compatible with COM, it didn't have a huge amount of flexibility: interfaces are mandated by the COM ABI to be called like this:
reference->vtable->method(reference)
This pretty much forces the vtable references to be stored in the object instance, one per implemented interface. Casting an instance to an interface means adding the offset to the vtable in the object data to the instance pointer.The vtables for every interface point at stubs which subtract the same offset and then jump to the implementation, so that object data is where it's expected for a normal instance method call.
COM influenced the internal design of Java interfaces too - the first three slots were reserved for COM's lifetime management and typecasting methods. I'm not sure anything practically ever came from that though.
I reused the interface calling mechanism when I implemented anonymous methods in Delphi. Anonymous methods can capture variables from their declaring scope; the captured state needs lifetime management in the absence of GC, so I piggy-backed on COM's lifetime management. This reduced the effort to interop with C++ too.
For example, fat pointers make it possible to implement interfaces for existing types. I have experienced that as game-changing in Haskell, Go, Rust.
I also disagree that any memory usage can be handwaved as “no longer relevant today”. Electron, or garbage collection: perhaps. But object size affects cache pressure, and cache utilization is critical to performance. C++ is typically good at keeping objects small, except when it comes to multiple inheritance.
As an aside, I noticed the word "klass", then looked up at the domain name and confirmed my suspicions. ;-)
https://dl.acm.org/citation.cfm?id=504291
Interface methods are looked up in a hash table containing function pointers (the table index can be computed at compile time). If there's a collision, a small stub is generated that dispatches to the right method.
I don't understand this when it doesn't have deoptimization?
The Java example has one condition for fast path, while the C# has two nested loops and a condition inside that!
But what about the worst case – the method lookup when the method is not in the cache? There, the CLR introduces an additional level of indirection for calls through interface types, which has benefits for memory usage and possibly dispatch time. But since this is the worst case, the complexity doesn't matter that much as long as the important cases (which can be cached) work well.
Thanks. Does the CLR do inline caching for interface calls which cannot be devirtualised without speculation? Or not because of the lack of deoptimisation? Or does it have self-modifying code instead?
> COLA is a system for experimenting with software design currently being investigated by the Viewpoints Research Institute. "COLA" stands for "Combined Object Lambda Architecture". A COLA is a self-describing language in two parts, an object system which is implemented in terms of objects, and a functional language to describe the computation to perform.
map[string]bool
map[string]struct{}
map[string]interface{} // where we put an empty struct in it.
results: BenchmarkBoolSet-4 5886572 223 ns/op 39 B/op 0 allocs/op
BenchmarkStructSet-4 6716685 206 ns/op 32 B/op 0 allocs/op
BenchmarkInterfaceSet-4 4299072 313 ns/op 115 B/op 0 allocs/op
(I'm aware this is a bit of a tangent to TFA) m := map[int]interface{}
// or m := map[int]struct{}
for i := 0; i < t.N; i++ {
m[i] = struct{}{}
}
edit: fix code.See http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.103... for more history and description of the implementation. From that paper: "The run-time system constructed an AbCon for each <type, implementation> pair that it encountered. An object reference consisted not of a single pointer, but of a pair of pointers: a pointer to the object itself, and a pointer to the appropriate AbCon". (Called an AbCon because it "mapped Abstract operations to Concrete implementations.)
One day I'll write a Pandoc filter that pipes these diagrams through a Shaky or Ditaa type tool first…
Looks weird for me on Mac desktop Chrome. It looks like the default monospace font, Courier, lacks box drawing characters. So it probably falls back to some other monospace font which likely has different metrics.
A good CSS font stack for monospace fonts is something like:
font-family: Consolas, Monaco, monospace;
The first catches almost all Windows users, the second almost all Mac users, and the last fallback to the default.The “book of the runtime” has a graph visualizing these possibilities: https://github.com/dotnet/coreclr/blob/master/Documentation/...
So this is a self-modifying code approach.
https://www.python.org/dev/peps/pep-0008/#function-and-metho...