implementing object oriented languages implementing
play

Implementing Object-Oriented Languages Implementing instance - PDF document

Implementing Object-Oriented Languages Implementing instance variable access Key features: Key problem: subtype polymorphism inheritance (possibly multiple) Solution: prefixing subtyping & subtype polymorphism layout of


  1. Implementing Object-Oriented Languages Implementing instance variable access Key features: Key problem: subtype polymorphism • inheritance (possibly multiple) Solution: prefixing • subtyping & subtype polymorphism • layout of subclass has layout of superclass as a prefix • message passing, dynamic binding, run-time type testing • code that accesses a superclass will access the superclass part of any subclass properly, transparently + access is just a load or store at a constant offset Subtype polymorphism is the key problem class Point { x • support uniform representation of data int x; (analogous to boxing for polymorphic data) int y; y • store the class of each object at a fixed offset } • organize layout of data to make instance variable access and method lookup & invocation fast class ColorPoint x extends Point { • code compiled expecting an instance of a superclass y Color color; still works if run on an instance of a subclass } • multiple inheritance complicates this color • perform static analysis to bound polymorphism // OK: subclass polymorphism • perform transformations to reduce polymorphism Point p = new ColorPoint(3,4,Blue); // OK: x and y have same offsets in all Point subclasses int manhattan_distance = p.x + p.y; Craig Chambers 232 CSE 501 Craig Chambers 233 CSE 501 Implementing dynamic dispatching (virtual functions) Virtual function tables How to find the right method to invoke for a dynamically Observation: in Option 3, all instances of a given class will have dispatched message rcvr.Message(arg1, ...) ? identical method addresses Option 1: search inheritance hierarchy, Option 4: factor out class-invariant parts into shared object starting from run-time class of rcvr • instance variables whose values are common across all − very slow, penalizes deep inheritance hierarchies instances of a class (e.g. method addresses) are moved out to a separate object • historically called a virtual function table (vtbl) Option 2: use a hash table • each instance contains a single pointer to the vtbl • can act like a cache on the front of Option 1 • combine with (or replace) class pointer − still significantly slower than a direct procedure call • layout of subclass’s vtbl has layout of superclass’s vtbl as a • but used in early Smalltalk systems! prefix Option 3: store method addresses in the receiver objects, + dynamic dispatching is fast & constant-time as if they were instance variables − but an extra load • each message/generic function declares an instance + no space cost in object variable • aside from vtbl/class pointer • each inheriting object stores an address in that instance variable • invocation = load + indirect jump! + good, constant-time invocation, independent of inheritance structure, overriding, ... − much bigger objects Craig Chambers 234 CSE 501 Craig Chambers 235 CSE 501

  2. Example of virtual function tables Multiple inheritance class Point { Problem: prefixing doesn’t work with multiple inheritance int x; int y; class Point { x void draw(); int x; int distance2origin(); int y; y } } table Point::draw draw class ColoredThing { color x ... d2o Color color; Point::d2o } y x color class ColorPoint class ColorPoint extends Point { extends Point, ? y x Color color; ColoredThing { color y } void draw(); void reverse_video(); } ColorPoint cp = new ColorPoint(3, 4, Blue); table ColorPt::draw draw Point p = cp; // OK x ... ColoredThing t = cp; // OK d2o ColorPoint cp2 = y r_v ColorPt::r_v new ColorPoint(p.x, p.y, t.color); // breaks color Craig Chambers 236 CSE 501 Craig Chambers 237 CSE 501 Some solutions Another solution Option 1: stick with single inheritance [e.g. Smalltalk] Option 4: embedding + pointer shifting [C++] − some examples really benefit from MI • concatenate superclass layouts, extend with subclass data • when upcasting to a superclass, shift pointer to point to where superclass is embedded Option 2: distinguish classes from interfaces [e.g. Java, C#] • downcasting does the reverse • only single inheritance below classes ⇒ if rcvr statically of class type, then can exploit • virtual function calls may need to shift rcvr pointers prefixing for its instance variable accesses and message • "trampolines" may get inserted sends • disallow instance variables in interfaces + gets back to constant-time access in most cases ⇒ no problems accessing them! • only messages to receivers of interface type are unresolved − very complicated, lots of little details ⇒ much smaller problem; can use e.g. hashing − some things (e.g. casting) may now have run-time cost − does poorly if using "virtual base classes", i.e., diamond- Option 3: compute offset of a field in rcvr by sending rcvr a shaped inheritance hierarchies message [Cecil/Vortex] − some sensible programs now disallowed • reduced problem to dynamic dispatching • e.g. casting through void*, downcasting from virtual base class • apply CHA etc. to optimize (all) dispatches − interior pointers may complicate GC, equality testing, ⇒ for fields whose offsets never change, debugging, etc. static binding + inlining reduces dispatches to constant Craig Chambers 238 CSE 501 Craig Chambers 239 CSE 501

  3. Example Example of virtual function tables class Point { class Point { class ColoredThing { x int x; int x; Color color; y int y; int y; void reverse_video(); } void draw(); } int d2o(); } class ColoredThing { color Color color; table } table draw r_v cp x d2o color x p class ColorPoint y extends Point, y ColoredThing { t color } class ColorPoint extends Point, ColoredThing { void draw(); ColorPoint cp = new ColorPoint(3, 4, Blue); } Point p = cp; // OK p table draw ColorPt::draw ColoredThing t = cp; // OK: adds 8 to cp x // now this works: d2o ColorPoint cp2 = y this = this + 12; r_v new ColorPoint(p.x, p.y, t.color); jump CT::r_v; t table // this works, too: ColorPoint cp3 = r_v color (ColorPoint) t; // subtracts 8 from t Craig Chambers 240 CSE 501 Craig Chambers 241 CSE 501 Limitations of table-based techniques Dynamic table-based implementations Table-based techniques only work well when: Standard implementation: global hash table in runtime system • indexed by class × msg • have static type information to use to map message/ • filled dynamically as program runs instance variable names to offsets in tables/objects • can be flushed after reflective operations • not true in dynamically typed languages + reasonable space cost + incremental • cannot extend classes with new operations except via − fair average-case dispatch time, poor worst-case time subclassing • not true in languages with open classes (e.g. MultiJava [Clifton et al. 00]) or multiple dispatching (e.g. CLOS, Dylan, Cecil) Refinement: hash table per message name • each call site knows statically which table to consult • cannot modify classes dynamically + faster dispatching • not true in fully reflective languages (e.g. Smalltalk, Self, CLOS) • memory loads and indirect jumps are inexpensive • may not be true with heavily pipelined hardware Craig Chambers 242 CSE 501 Craig Chambers 243 CSE 501

  4. Inline caching Example of inline caching Give each dynamically-dispatched call site its own Initially: small method lookup cache + call site knows its message name ... + cache is isolated from other call sites call Lookup msg: “draw” class: Trick: use machine call instruction itself as a one-element cache ... • initially: call runtime system’s Lookup routine • Lookup routine patches call instruction to branch to invoked method • record receiver class • next time through, jump directly to expected target method After caching target method: • method checks whether current receiver class is same as last receiver class ... ColorPoint::draw() • if so, then cache hit (90-95% frequency, for Smalltalk) if cache.class ≠ call Lookup • if not, then call Lookup and rebind cache self.class then msg: “draw” class: CPt call Lookup + fast dispatch sequence if cache hit ( ≈ 4 instructions plus call) ... regular code ... ... + hardware call prefetching works well − exploits self-modifying code − low performance if not a cache hit [Deutsch & Schiffman 84] Craig Chambers 244 CSE 501 Craig Chambers 245 CSE 501 Polymorphic inline caching (PIC) Example of polymorphic inline caching Idea: support a multi-element cache by generating a After a few receiver classes: call-site-specific dispatcher stub + fast dispatching even if several classes are common ... − still slow performance if many classes equally common call Lookup − some space cost msg: “draw” ... Foreshadowing: dispatching stubs record dynamic profile data of which receiver classes occur at which call sites switch (self.class) { ColorPt::draw() case ColorPt: case ColorPt3D: case Point: [Hölzle et al. 91] Point::draw() default: call Lookup } Craig Chambers 246 CSE 501 Craig Chambers 247 CSE 501

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend