240 likes | 337 Views
Java Subtype Tests in Real-Time. Krzysztof Palacz, Jan Vitek University of Purdue Presented by: Itay Maman. Outline. Subtyping tests Previous work R&B Overview Ranges (SI test) Buckets (MI test) Results Conclusions & Future Research. Subtyping tests. Does a type extend a given class?
E N D
Java Subtype Tests in Real-Time Krzysztof Palacz, Jan Vitek University of Purdue Presented by: Itay Maman
Outline • Subtyping tests • Previous work • R&B Overview • Ranges (SI test) • Buckets (MI test) • Results • Conclusions & Future Research
Subtyping tests Does a type extend a given class? Does a type implement a given interface? • Given a hierarchy (T,≺) • T is a set of types • ≺ is a partial order over T (reflexive, transitive and anti-symmetric) called subtype relation • The query c ≺ p is a subtype test • In the above test, C is the Client type, while P is the Provider type • Java: • Class inheritance test (“extends”) is an SI subtype test • Interface inheritance test (“implements”) is an MI subtype test
Subtyping tests - Requirements • Queries must run in constant time • Space overhead must not significantly increase system footprint • Size of emitted code • Memory needed for the data structure • Support for dynamic loading of classes • Ideally, should be able to load a class without blocking concurrent subtype queries, invoked by other threads
Naïve solution subtypeOf(type_info cl, type_info pr) { if(cl == pr || cl.cache == pr) return true; if(pr.isInterface return implements(cl, pr); else return extends(cl, pr); } implements(type_info cl, type_info pr) { for(int i = 0; i < pr.interfaces.length; ++i) if(cl == pr.interfaces[i]) { cl.cache = pr; return true; } return false; } extends(type_info cl, type_info pr) { for(type_info t = cl.parent; t != null; t = t.parent) if(t == pr) { cl.cache = pr; return true; } return false; }
Previous Work • SI (single inheritance) hierarchies • Bit matrix – Space inefficient • Relative numbering [Schubert ’83] • Cohen's algorithm [Cohen ’91] • MI (multiple inheritance) hierarchies • Packed Encoding (PE) - generalization of Cohen's algorithm [Krall, Vitek and Horspool ’97] • Bit-vectors [Krall, Vitek and Horspool ’97a] • PQ Encoding – Adapts Relative numbering to MI [Zibin, Gil ’01] • Incremental technique in production JVMs (HotSpot, Jalapeno) • SI: Cohen’s algorithm with arrays of a fixed size (inlined) • MI: Linear search over a list of displays
[0,8] A B C [1,3] [4,7] D F E [2,0] [5,0] [6,0] Ranges (SI test): The basics • Based on Scubert’s technique • Ranges of children are subranges of their parents’ • Ranges of siblings are disjoint • Range assignment: Via a pre-order walk • c≺pp.low ≤ c.low < p.high
The high bound can be calculated on-demand. When a type is loaded it is initialized with 0 Ranges (SI test): Refinements • Only the low bound is observed for the client • A leaf type can reuse its parent’s low bound Reminder: c≺pp.low ≤ c.low < p.high Conclusion: insert(type_info t) { t.high = 0; t.low = (t.parent == null) ? 1 : t.parent.low; }
Ranges (SI test): extends() • extends() implements an SI test • If the provider’s high bound is not present, its value is computed by invoking promote() extends(type_info cl, type_info pr) { if(pr.low <= cl.low && cl.low < pr.high) return true; if(pr.high != 0) return false; promote(pr); return (pr.low <= cl.low && cl.low < pr.high); }
A B C D F E Ranges (SI test): promote(), 1/3 Steps for computing range assignments: • Step 1. Place all type_info items in an array using a pre-order walk • Leaf types are stored once • Non-leaves are stored twice: once before all their subtypes, and once after type_info:
A B C D F E Ranges (SI test): promote(), 2/3 • Step 2. Perform a left-to-right iteration over the array. Compute order[i] for each index • Start with 1 • Increase whenever a non-leaf type is encountered order: type_info:
A C.h = 5 B.h = 3 E.l = 4 B.l = 2 B C A.l = 1 C.l = 4 F.l = 4 D.l = 2 A.h = 6 D F E Ranges (SI test): promote(), 3/3 • Perform a right-to-left iteration over the array (end to beginning) • Right position of a non-leaf type: assign order[i] to its high bound • Left position of a non-leaf type: assign order[i] to its low bound • Leaf type: assign order[i] to its low bound order: [1,6] [2,3] [4,5] [2,0] [4,0] [4,0] type_info:
Promote() is thread-safe Ranges (SI test): Thread-safety Given the following properties of the promote() algorithm… • A type’s high bound is updated before its subtypes are processed • A type’s high bound is updated before its low bound • Sibling types are processed on a descending order of their low bounds … It is guaranteed that the invariants of Scubert’s technique are kept at any given point during its operation: • Ranges of children are subranges of their parents’ • Ranges of siblings are disjoint Conclusion:
Ranges (SI test): Summary • Low, constant memory demands (32 bits per class) • Query time • Is constant in vast majority of the tests • In RT systems, promote() can be used eagerly (invoked on class load), to ensure constant query time • Code is thread-safe => can handle dynamically loaded classes. No need to “stop the world” • Performance improvement of: ** ??? **
Buckets (MI test): The basics • Based on the Packed-Encoding algorithm (PE) • Each interface is assigned to a bucket, and receives a unique id (iid) within that bucket • Two interfaces in a bucket do not have a common subtype • c≺pc.display[p.bucket] ==p.iid type_info { … byte[] display; } interface_info { byte iid; byte bucket; }
Buckets (MI test): assignment 1/3 Computing bucket assignments: • Case 1. Loading an interface. • If all buckets are full or no bucket exists, create a new bucket • Choose the bucket with the fewest interfaces among ‘M’ most recently created buckets. (Typically, M = 5) • Create a new iid value for the interface, which is unique within the bucket. • A bucket cannot reuse ‘old’ (i.e: previously used) iid-s
Buckets (MI test): assignment 2/3 • Case 2. Loading a class. • If the interfaces implemented by the class are of different buckets, initialize the array of displays: cl.display[i.bucket] = i.iid; // for each implemented interface i • Otherwise, the class is a subtype of (at least) two interfaces of the same bucket. These interfaces must be reassigned to other buckets (Details on the next slide)
Buckets (MI test): assignment 3/3 • Case 2 contd. Loading a class/Reassignment • For each bucket b, with k interfaces implemented by a class C: • Create k-1 new buckets • k-1 interfaces are assigned to the new buckets • Remaining interfaces from b are evenly spread among b and the new buckets An interface’s iid is not changed when the interface is reassigned A bucket cannot reuse an iid of an interfaces that was reassigned • Iterate over all loaded classes and update their display[] array: • Existing entries remain unchanged • New entries are added for the new buckets • Consequently, in a given class’s display[] an iid of the same interface may appear more than once. • Iterate over all reassigned interfaces, update their bucket value
Buckets (MI test): Thread-safety Given the following properties of the Buckets algorithm… • An interface never changes its iid • A bucket cannot reuse an ‘old’iid • Updated display[] arrays contain the old entries as well as the new ones • An interface’s bucket value is changed only after display[] s are updated … It is guaranteed that an implements() query will always yield the correct result
Definitions • ≺d is the transitive reduction of ≺ • ≺ is the transitive closure of ≺d • Formally, a≺d b iff a ≺ b and there is no c such that a ≺ c ≺ b, a≠c≠b. • Also, • ancestors(a)≡{b∈T| a ≺ b}, descendants(a)≡{b∈T| b ≺ a} • parents(a)≡{b∈T| a ≺d b}, children(a)≡{b∈T| b ≺d a} • roots≡{a∈T| parents(a)=∅}, leaves≡{a∈T| children(a)=∅} • level(a)≡1+max{level(b)| b∈parents(a)} • Single inheritance (SI) vs. multiple inheritance (MI) • In SI, for each a∈T, |parents(a)|≤1
Cohen's algorithm • Partition the hierarchy into levels • a≺blb≤ la and ra[lb] =idb • lbis level(b), idb is a unique identifier within the level
Range compression • Apply postorder on some spanning forest • a ≺ b lb[i]≤ida ≤rb[i] , for some i {1,2,3} {2,5,6}
Ranges (SI test): promote() Computes range assignments: • Place all types in an array using a pre-order walk • Leaf types are stored once • Non-leaves are stored twice: once before all their subtypes, and once after • Perform a left-to-right iteration over the array. Compute order[i] for each index • Start with 1 • Increase whenever a non-leaf type is encountered • Perform a right-to-left iteration over the array (end to beginning) • Leaf type: assign (order[i], 0) to (low, high) • Right position of a non-leaf type: assign order[i] to its high bound • Left position of a non-leaf type: assign order[i] to its low bound