Speculative Devirtualization

I know that Swift has an optimization called “speculative devirtualization,” and I can guess roughly what it probably does but I’d, um,… prefer not to speculate :sweat_smile:. Can anybody fill me in on the nitty gritty?

Thanks,
Dave

1 Like

Hi Dave,

This inserts a type check (or series of checks) before a polymorphic call, conditionally replacing it with direct dispatch to the statically known call targets (up to six it seems).

I suspect this is beneficial because it’s our only way of dispatching to specialized code through a polymorphic call.

In exchange, it increases code size and, given the type checks, won’t do much to expose optimization in the caller. I would prefer to support dynamic dispatch to specialized code via specializaed witness tables, but I don’t see that happening anytime soon.

Andy

2 Likes

Thanks @Andrew_Trick , that’s very helpful.

Now, can you explain what you mean by “specialized witness tables?” Since witness tables provide the type-specific implementations of generic requirements, it seems to me that the implementation in the table always have to be “specialized” in some sense. Otherwise, they beg the very question they exist to answer, no?

You’ve just made the case that “specialize witness tables” are possible. However, when a protocol conformance is satisfied by a generic type, it’s the generic implementation of the methods that are picked up by the witness table.

   protocol FrameworkType {
     func action()
   }

   func doAction(f: FrameworkType) {
     f.action()
   }

   //--- Framework boundary

   struct AppType<T> : FrameworkType {
     var t: T
     init(t: T) { self.t = t }
     func action() {
       print(t)
     }
   }

   func run() {
     doAction(AppType(t: 3)) // calls generic print.
   }

As a stopgap solution, we introduced the @_specialize annotation to do a type check and specialized dispatch on the callee side (it complements speculative devirtualization).

Thanks for explaining, Andy. I didn’t realize this was just about witness tables for generic type instantiations, and I also didn’t know we weren’t using the specialized tables—which do exist, right?—in these cases! :sob:

I’ve never been keen on @_specialize but I’m hoping I’m wrong and it’s actually turned out to be more effective than I thought it would. Do we have any information on how it’s working out?

@specialize does what it’s supposed to do, but we would like to never need to use it. It’s needed in a few places in the stdlib. When it’s the only way, I think it’s very effective because specializing code is very effective. It’s obviously limited to the situation in which the library author knows a-priori which types need to be instantiated for performance.