10 likes | 211 Views
Making. IT. Computing Science. happen. Read Me First. Single dispatch. Multiple dispatch. Multiple dispatch. Multi-Dispatch in the Java Virtual Machine. TM. Christopher Dutchyn. Paul Lu. Duane Szafron. Steve Bromling. Wade Holst. What is (dynamic) multi-dispatch?.
E N D
Making IT Computing Science happen Read Me First Single dispatch Multiple dispatch Multiple dispatch Multi-Dispatch in the Java Virtual Machine TM Christopher Dutchyn Paul Lu Duane Szafron Steve Bromling Wade Holst What is (dynamic) multi-dispatch? Previously, how did people work around this problem? Multi-Dispatch AWT and Swing Visitor Pattern (Double Dispatch) Type Testing Type Numbering • Method selection based on the types of • … all (or more than one)of the arguments • … at execution time (not compile time). • We modified Swing and AWT to use multi-dispatch: • changed 92 out of 846 classes • removed 123 custom-coded dispatcher methods • eliminated 171 decision points (if/else and switch/case statements) • reduced average decisions in each changed method from 3.8 to 2.0 • replaced 4.74 million dispatches with 2.35 million multi-dispatches • … with faster dispatch class Person { static final int PERSON_TYPE = 0; protected int id = PERSON_TYPE; } class Student extends Person{ static final int STUDENT_TYPE = 1; Student() { this.id = STUDENT_TYPE }; } class Theatre { String admission(Person p) { switch (p.id) { case PERSON_TYPE: return "pays $10"; case STUDENT_TYPE: return "pays $5"; }} class Orchestra { String admission(Person p) { switch (p.id) { case PERSON_TYPE: return "pays $40"; case STUDENT_TYPE: return "pays $25"; }} abstract class Visitor { abstract String visitTheatre(Theatre t); abstract String visitOrchestra(Orchestra o); } class Person extends Visitor { String visitTheatre(Theatre t) {return"pays $10";} String visitOrchestra(Orchestra o){return"pays $40";}} class Student extends Person { String visitTheatre(Theatre t) {return"pays $5";} String visitOrchestra(Orchestra o){return"pays $25";}} abstract class Venue { abstract String admission(Visitor v); } class Theatre extends Venue { String admission(Visitor v){return v.visitTheatre(this); }} class Orchestra extends Venue { String admission(Visitor v) {return v.visitOrchestra(this);}} abstract class Venue { abstract String studentAdmission(); abstract String personAdmission(); String admission(Person p) { if (p instanceof Student) {return studentAdmission();} else if (p instanceof Person){return personAdmission();} }} class Theatre extends Venue { String personAdmission() {return "pays $10";} String studentAdmission() {return "pays $5"; }} class Orchestra extends Venue { String personAdmission() {return "pays $40";} String studentAdmission() {return "pays $25";}} Is this still OO? class Person { /* … */ } class Student extends Person { /* … */ } class Theatre { String admission(Person p) {return "pays $10";} String admission(Student s) {return "pays $5"; }} class Orchestra { String admission(Person p) {return "pays $40";} String admission(Student s) {return "pays $25";}} Given these definitions: Duane: pays $10 and pays $40. Chris: pays $10 and pays $40. Statically, chris is a ‘Person’. What does this code return? • Difficulties: • overhead: second method invocation • overhead: instanceof is costly • error-prone: always test subtypes first • Difficulties: • overhead: second method invocation • maintainability: custom dispatch code • clarity: logic spread across all classes • Type Testing that trades extensibility and maintainability of OO for performance: • replicated switch « second method invocation • instanceof « manifest constants Duane: pays $10 and pays $40. Chris: pays $5 and pays $25. Theatre t = new Theatre(); Orchestra o = new Orchestra(); Person duane = new Person("Duane"); Person chris = new Student("Chris"); System.out.println(duane + ": " + t.admission(duane) + " and " + o.admission(duane) + "." ); System.out.println(chris + ": " + t.admission(chris) + " and " + o.admission(chris) + "." ); How efficient is it? Dynamically, chris is a ‘Student’. High-performance SRP+ multi-dispatcher operates faster than double-dispatching in the Java interpreter (tested on Sun classic VM for Linux): What does the multi-dispatch version look like? Isn’t this method overloading? Advantages: • No second method invocation • No custom written dispatch code • No type tests • No type numbering • Extensible without changing existing libraries Implement empty marker interface (like Cloneable) signaling JVM to multi-dispatch this class class Person { /* … */ } class Student extends Person { /* … */ } class Theatre implements VirtualMultiDispatchable { String admission(Person p) {return "pays $10";} String admission(Student s) {return "pays $5"; }} class Orchestra implements VirtualMultiDispatchable { String admission(Person p) {return "pays $40";} String admission(Student s) {return "pays $25";}} The Container Problem still applies; static multi-dispatch (method overloading) isn’t enough, nor are parametric types: Theatre t = new Theatre(); Orchestra o = new Orchestra(); Person duane = new Person("Duane"); Person chris = new Student("Chris"); System.out.println(duane + ": " + t.admission(duane) + " and " + o.admission(duane) + "." ); System.out.println(chris + ": " + t.admission(chris) + " and " + o.admission(chris) + "." ); Method overloading requires the compiler to know the type of lineUp[i]. The compiler sees them all as type ‘Person’; the container obscures the dynamic type of ‘Student’ elements. The programmer cannot simply insert a cast to ‘Student’ – some elements are ‘Person’. Duane: pays $10 and pays $40. Chris: pays $5 and pays $25. Person lineUp[] = { new Student("Chris"), new Person("Paul"), new Person("Duane"), new Student("Steve"), new Person("Wade") }; for (int i=0; i<lineUp.length; i++) { System.out.println(lineUp[i] + ": " + t.admission(lineUp[i]) + " and " + o.admission(lineUp[i]) + "." ); } Without language extensions or custom dispatch code, the programmer can target multi-dispatch to only the classes that require this technique. How does the JVM perform a multi-dispatch? What are the benefits of this multi-dispatch approach? But, how common is this problem? • No language extensions or preprocessors: • maintains tool support: debuggers, profilers work on as-written code • Java source and binary compatible: we compile with the original javac • extend third-party components to use multi-dispatch without source • maintains reflection API – methods appear exactly as written • Programmer-targeted multi-dispatch: use it only where needed • single dispatch has unchanged performance and semantics • Full JVM support: • multi-dispatch instance and static methods (separately selectable) • multi-dispatch native and private methods, constructors, and supercalls • add multi-dispatch to languages that emit .class files (Eiffel, Ada) • Better performance than custom type-numbers without sacrificing OO Theatre t = new Theatre(); Orchestra o = new Orchestra(); Person duane = new Person("Duane"); Person chris = new Student("Chris"); System.out.println(duane + ": " + t.admission(duane) + " and " + o.admission(duane) + "." ); System.out.println(chris + ": " + t.admission(chris) + " and " + o.admission(chris) + "." ); This occurs frequently enough that a solution (Visitor) was included in the original GOF design patterns; common situations include: • At a method invocation, the JVM has • the message selector • the arguments on a stack • the list of potential methods … equivalent bytecode Binary Operations Drag and Drop Event Programming The heart of AWT (and Swing) is dispatching a queue of various kinds of events to a variety of GUI components. Drag and drop is the prototypical situation requiring dispatch on multiple arguments. The actions of the drop target depend on the kind of target and the kind of object transferred. Many operations, such as merges and comparisons, depend intimately on the types of their operands. bytecode … // bytecode before t.admission(chris) aload 1 // push t aload 2 // push chris invokevirtual #6 // method Theatre.admission(Person)String … // bytecode after t.admission(chris) stack Student chris Theatre t … method dictionary • Behavior admission • Theatre, Person ® String • Theatre, Student ® String • Orchestra, Person ® String • Orchestra, Student ® String [ ^"pays $10" ] class AWTEvent { /* … */ } class MouseEvent extends AWTEvent { /* … */ } class KeyboardEvent extends AWTEvent { /* … */ } class FocusEvent extends AWTEvent { /* … */ } … class Component { void processEvent(AWTEvent e) { /* … */ } } class Window extends Component { /* … */ } class Button extends Component { /* … */ } class ScrollBar extends Component { /* … */ } class JComponent extends Component { /* … */ } … class CustomerList { CustomerList merge(CustomerList c) { //O(nlogn) return this.sort().merge(c.sort()); } CustomerList merge(SortedCustomerList s) { //O(nlogn) return this.sort().merge(s); } } class SortedCustomerList extends CustomerList { CustomerList merge(CustomerList c) { //O(nlogn) return this.merge(c.sort()); } CustomerList merge(SortedCustomerList s) { //O(n) /* actually merge in linear time */ } } [ ^"pays $5" ] … so the JVM can determine the types of the stacked arguments select another method that more closely matches the argument types class Viewer{ DropTargetListener vDropListener; …} class Printer {DropTargetListener pDropListener; …} class TextDocument implements Transferable {/* … */ } class Spreadsheet implements Transferable {/* … */ } class GraphicsImage implements Transferable {/* … */ } [ ^"pays $40" ] [ ^"pays $25" ] Native multi-dispatch in the Java Virtual Machine