1

While this is likely more broadly applicable, I am working in javascript, and primarily optimizing for V8, and therefore interested in the situation there.

When trying to avoid code duplication, a situation often arises, where base functionality needs to interact with custom code for various extensions. For this topic, the distinction of interest is:

  • Embedding dynamic features into one base:

    const base = extension => {
      // Base code.
      extension();
      // Base code.
    };
    

    Also all stereotypical uses of polymorphism, e.g. passing an interface.

    (extensions on top, base at bottom)

    topdown

  • Embedding base features into extensions:

    declare const base: () => void;
    const extension1 = () => {
      // Extension code.
      base();
      // Extension code.
    };
    

    (base on top, extensions at bottom)

    topdown


From what I understand, V8 will in all normal cases only have one compile per function code body. This means, that in the first case:

  • Inlining the extensions isn't feasible, and accordingly escape analysis is problematic.
  • For passing interfaces, inline caches need to deal with all possible shapes.
  • ...?

The second option isn't always possible, e.g. for canonical polymorphism, where a distinction between multiple extensions needs to be made in some way, or if the different extensions/shapes/cases are dynamic, and not a handful known at compile-time. However, there are many cases, where usage of extensions is a partition of the code-base, and local code need not worry about there potentially being a different one.

Tl;dr; from a performance view, in a polymorphic scenario, if possible, it is preferable to embed a base into extensions, not the other way around. Is this observation correct? Are there some larger caveats I am missing, e.g. a "yes, but in praxis..."?


PS: I am in some way prematurely optimizing (worrying about something, without having a benchmark indicating a bottleneck), but also feel knowing the potential impact is separate from then acting upon it, or not. For some cases in inner loops, the switch to real function calls, temporary objects due to missing escape analysis, etc, could be problematic.

Doofus
  • 952
  • 4
  • 19
  • "*Inlining the extensions isn't feasible*" - it might be, if the whole `base(myExtension)` call is inlined. But yeah, I'd love to have closures that use different inline caches per instance and might be optimise-compiled to different native code… – Bergi Oct 17 '22 at 20:07
  • True, inlining the whole thing may work, although I am not immediately sure, whether there are some problems involved with that. Would be interesting to know, as it might negate a lot of the problem. That's exactly why i ask this, I'd prefer not memorizing some concept, which in reality doesn't apply, because of something I didn't know. – Doofus Oct 17 '22 at 23:52

0 Answers0