Java Subtype Tests in Real-Time by tfu54501

VIEWS: 5 PAGES: 24

									Java Subtype Tests in Real-Time


     Krzysztof Palacz, Jan Vitek
        University of Purdue
        Presented by: Itay Maman



                                   1
       Outline

   Subtyping tests
   Previous work
   R&B Overview
   Ranges (SI test)
   Buckets (MI test)
   Results
   Conclusions & Future Research




                                    2
         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

                                                                                3
         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




                                                                              4
   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; }


                                                           5
           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


                                                                              6
         Ranges (SI test): The basics

   Based on Scubert‟s technique
    1) Ranges of children are subranges of their parents‟
    2) Ranges of siblings are disjoint



   Range assignment: Via a pre-order walk
        c ≺ p  p.low ≤ c.low < p.high
                                   [0,8]
                              A
                  [1,3]   B               C   [4,7]


                  [2,0]   D       E           F
                                  [5,0]       [6,0]
                                                            7
     Ranges (SI test): Refinements

        Reminder:
        c ≺ p  p.low ≤ c.low < p.high

   Only the low bound is observed for the client
   A leaf type can reuse its parent‟s low bound

Conclusion:     The high bound can be calculated on-demand.
                When a type is loaded it is initialized with 0

    insert(type_info t) {
       t.high = 0;
       t.low = (t.parent == null) ? 1 : t.parent.low;
    }


                                                                 8
         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);
    }

                                                           9
         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


         A
 B               C       type_info:
                                  A   B    D    B   C    E    F    C    A
 D           E       F
                                  0   1    2    3   4    5    6    7    8

                                                                        10
             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:
                                       1   2   2   3    4   4    4   5   6
              A
    B                 C       type_info:
                                       A   B   D   B   C    E    F   C   A
    D             E       F
                                       0   1   2   3    4   5    6   7   8

                                                                         11
             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]        A
                                          1      2      2      3     4       4    4        5      6
                          [4,5]
    B                 C
                                                 B.l = 2       B.h = 3       E.l = 4           C.h = 5

                                       A.l = 1       D.l = 2       C.l = 4       F.l = 4         A.h = 6
    D            E         F
   [2,0]      [4,0]         [4,0]
                          type_info:      A      B      D      B     C       E    F        C      A
                                          0      1      2      3     4       5    6        7      8      12
        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:
   1)    Ranges of children are subranges of their parents‟
   2)    Ranges of siblings are disjoint


Conclusion:   Promote() is thread-safe

                                                                       13
         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: ** ??? **




                                                                       14
        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 ≺ p  c.display[p.bucket] == p.iid
type_info { …
    byte[] display; }


interface_info {
    byte iid;
    byte bucket; }

                                                                 15
      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



                                                              16
        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)




                                                               17
          Buckets (MI test): assignment 3/3

   Case 2 contd. Loading a class/Reassignment
    1)    For each bucket b, with k interfaces implemented by a class C:
          a) Create k-1 new buckets
          b)  k-1 interfaces are assigned to the new buckets
          c) 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

    2)    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.
    3)    Iterate over all reassigned interfaces, update their bucket value
                                                                                   18
        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




                                                                         19
The END




          20
          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



                                                                  21
                   Cohen's algorithm

       Partition the hierarchy into levels
            a ≺ b  lb ≤ la and ra[lb] = idb
            lb is level(b), idb is a unique identifier within the level
            r[1]
                    id                           1 1         1
         ...




    l                                                                A
            r[l]


                                 1                   1                               1
                             2       1           2           2               2               3
                                 1       B           2               C               3               D

             1                   1               1                       1                       1
    3        1       1       3   1   2       3   1       3       3       3       4       3       3       5
             1           E       2       F       3           G           4           H           5           I

                                                                                                                 22
           Range compression

   Apply postorder on some spanning forest
        a ≺ b  lb[i] ≤ ida ≤ rb[i] , for some i

    [l1,r1],[l2,r2],...           [1,6]                [1,3],[5,9]
                                            4                          9
          id                                      A                          B

                          [1,3]                 [2,2],[5,6]                [5,5],[7,8]
                 {1,2,3}
                                    3             {2,5,6}
                                                               6                         8
                                           C                         D                       E

         [1,1]                            [2,2]                      [5,5]                   [7,7]
                   1                               2                          5                      7
                           F                            G                         H                      I
                                                                                                             23
         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

                                                                                  24

								
To top