Changeset 3476 in josm


Ignore:
Timestamp:
Aug 29, 2010 12:58:38 PM (3 years ago)
Author:
jttt
Message:

QuadBuckets optimization - keep children as fields instead of 4-item array

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/QuadBuckets.java

    r3453 r3476  
    2020    private static boolean debug = false; 
    2121    private static final boolean consistency_testing = false; 
     22    private static final int NW_INDEX = 1; 
     23    private static final int NE_INDEX = 3; 
     24    private static final int SE_INDEX = 2; 
     25    private static final int SW_INDEX = 0; 
    2226    /* 
    2327     * Functions prefixed with __ need locking before 
     
    6266        final long quad; 
    6367        final QBLevel parent; 
     68        private boolean isLeaf = true; 
    6469 
    6570        public List<T> content; 
    66         public QBLevel children[]; 
     71        public QBLevel nw, ne, sw, se; 
     72 
     73        private QBLevel getChild(int index) { 
     74            switch (index) { 
     75            case NE_INDEX: 
     76                if (ne == null) { 
     77                    ne = new QBLevel(this, index); 
     78                } 
     79                return ne; 
     80            case NW_INDEX: 
     81                if (nw == null) { 
     82                    nw = new QBLevel(this, index); 
     83                } 
     84                return nw; 
     85            case SE_INDEX: 
     86                if (se == null) { 
     87                    se = new QBLevel(this, index); 
     88                } 
     89                return se; 
     90            case SW_INDEX: 
     91                if (sw == null) { 
     92                    sw = new QBLevel(this, index); 
     93                } 
     94                return sw; 
     95            default: 
     96                return null; 
     97            } 
     98        } 
     99 
     100        private QBLevel[] getChildren() { 
     101            // This is ugly and hackish.  But, it seems to work, 
     102            // and using an ArrayList here seems to cost us 
     103            // a significant performance penalty -- 50% in my 
     104            // testing.  Child access is one of the single 
     105            // hottest code paths in this entire class. 
     106            QBLevel[] result = (QBLevel[]) Array.newInstance(this.getClass(), QuadTiling.TILES_PER_LEVEL); 
     107            result[NW_INDEX] = nw; 
     108            result[NE_INDEX] = ne; 
     109            result[SW_INDEX] = sw; 
     110            result[SE_INDEX] = se; 
     111            return result; 
     112        } 
     113 
    67114        @Override 
    68         public String toString() 
    69         { 
     115        public String toString()  { 
    70116            return super.toString()+ "["+level+"]: " + bbox(); 
    71117        } 
     
    117163                if (index == -1) 
    118164                    return this; 
    119                 if (children[index] == null) { 
    120                     children[index] = new QBLevel(this, index); 
    121                 } 
    122                 return children[index].findBucket(bbox); 
     165                return getChild(index).findBucket(bbox); 
    123166            } 
    124167        } 
     
    144187            } 
    145188        } 
    146         QBLevel[] newChildren() 
    147         { 
    148             // This is ugly and hackish.  But, it seems to work, 
    149             // and using an ArrayList here seems to cost us 
    150             // a significant performance penalty -- 50% in my 
    151             // testing.  Child access is one of the single 
    152             // hottest code paths in this entire class. 
    153             return (QBLevel[])Array.newInstance(this.getClass(), QuadTiling.TILES_PER_LEVEL); 
    154         } 
    155189        // Get the correct index for the given primitive 
    156190        // at the given level.  If the primitive can not 
     
    190224                        + this.bbox().width()+", "+this.bbox().height()+")"); 
    191225            } 
    192             // deferring allocation of children until use 
    193             // seems a bit faster 
    194             //for (int i = 0; i < TILES_PER_LEVEL; i++) 
    195             //    children[i] = new QBLevel(this, i); 
    196226            List<T> tmpcontent = content; 
    197227            content = null; 
    198             children = newChildren(); 
     228 
    199229            for (T o: tmpcontent) { 
    200                 add(o); 
    201             } 
    202             if (!hasChildren()) { 
    203                 // All items stay at this level (get_index == -1). Create at least first child to keep the contract 
    204                 // that at least one child always exists 
    205                 children[0] = new QBLevel(this, 0); 
    206             } 
     230                int index = get_index(o.getBBox(), level); 
     231                if (index == -1) { 
     232                    __add_content(o); 
     233                } else { 
     234                    getChild(index).doAdd(o); 
     235                } 
     236            } 
     237            isLeaf = false; // It's not enough to check children because all items could end up in this level (index == -1) 
    207238        } 
    208239 
     
    254285         * is fast.  Runtime type determination must be slow. 
    255286         */ 
    256         boolean isLeaf() 
    257         { 
    258             if (children == null) 
    259                 return true; 
    260             return false; 
    261         } 
     287        boolean isLeaf() { 
     288            return isLeaf; 
     289        } 
     290        boolean hasChildren() { 
     291            return nw != null || ne != null || sw != null || se != null; 
     292        } 
     293 
    262294        QBLevel next_sibling() 
    263295        { 
     
    265297            if (parent == null) 
    266298                return null; 
    267             if (parent.children == null) 
    268                 return null; 
    269299            int __nr = 0; 
    270             for (QBLevel sibling : parent.children) { 
     300            for (QBLevel sibling : parent.getChildren()) { 
    271301                __nr++; 
    272302                int nr = __nr-1; 
     
    325355        { 
    326356            QBLevel ret = null; 
    327             for (QBLevel child : this.children) { 
     357            for (QBLevel child : getChildren()) { 
    328358                if (child == null) { 
    329359                    continue; 
     
    358388                    get_index(o.getBBox(), level-1); 
    359389                    int nr = 0; 
    360                     for (QBLevel sibling : parent.children) { 
     390                    for (QBLevel sibling : parent.getChildren()) { 
    361391                        out("sibling["+ (nr++) +"]: " + sibling.bbox() + " this: " + (this==sibling)); 
    362392                    } 
     
    389419                } 
    390420                return; 
    391             } 
     421            } else if (bbox().bounds(search_bbox)) { 
     422                search_cache = this; 
     423            } 
     424 
    392425            if (this.hasContent()) { 
    393426                search_contents(search_bbox, result); 
     
    403436                out("[" + level + "] not leaf, moving down"); 
    404437            } 
    405             for (int i = 0; i < children.length; i++) { 
    406                 QBLevel q = children[i]; 
    407                 if (debug) { 
    408                     out("child["+i+"]: " + q); 
    409                 } 
    410                 if (q == null) { 
    411                     continue; 
    412                 } 
    413                 if (debug) { 
    414                     System.out.print(i+": "); 
    415                 } 
    416                 q.search(search_bbox, result); 
    417                 if (q.bbox().bounds(search_bbox)) { 
    418                     search_cache = q; 
    419                     // optimization: do not continue searching 
    420                     // other tiles if this one wholly contained 
    421                     // what we were looking for. 
    422                     if (debug) { 
    423                         out("break early"); 
    424                     } 
    425                     break; 
    426                 } 
     438 
     439            //TODO Coincidence vector should be calculated here and only buckets that match search_bbox should be checked 
     440 
     441            if (nw != null) { 
     442                nw.search(search_bbox, result); 
     443            } 
     444            if (ne != null) { 
     445                ne.search(search_bbox, result); 
     446            } 
     447            if (se != null) { 
     448                se.search(search_bbox, result); 
     449            } 
     450            if (sw != null) { 
     451                sw.search(search_bbox, result); 
    427452            } 
    428453        } 
     
    436461                return -1; 
    437462 
     463            QBLevel[] children = getChildren(); 
    438464            for (int i = 0; i < QuadTiling.TILES_PER_LEVEL; i++) { 
    439465                if (children[i] == find_this) 
     
    461487            return QuadTiling.tile2LatLon(this.quad); 
    462488        } 
    463         boolean hasChildren() 
    464         { 
    465             if (children == null) 
    466                 return false; 
    467  
    468             for (QBLevel child : children) { 
    469                 if (child != null) 
    470                     return true; 
    471             } 
    472             return false; 
    473         } 
    474489        void remove_from_parent() 
    475490        { 
     
    477492                return; 
    478493 
    479             int nr_siblings = 0; 
    480             for (int i = 0; i < parent.children.length; i++) { 
    481                 QBLevel sibling = parent.children[i]; 
    482                 if (sibling != null) { 
    483                     nr_siblings++; 
    484                 } 
    485                 if (sibling != this) { 
    486                     continue; 
    487                 } 
    488                 if (!canRemove()) { 
    489                     abort("attempt to remove non-empty child: " + this.content + " " + Arrays.toString(this.children)); 
    490                 } 
    491                 parent.children[i] = null; 
    492                 nr_siblings--; 
    493             } 
    494             if (nr_siblings == 0) { 
    495                 parent.children = null; 
    496             } 
     494            if (!canRemove()) { 
     495                abort("attempt to remove non-empty child: " + this.content + " " + Arrays.toString(this.getChildren())); 
     496            } 
     497 
     498            if (parent.nw == this) { 
     499                parent.nw = null; 
     500            } else if (parent.ne == this) { 
     501                parent.ne = null; 
     502            } else if (parent.sw == this) { 
     503                parent.sw = null; 
     504            } else if (parent.se == this) { 
     505                parent.se = null; 
     506            } 
     507 
    497508            if (parent.canRemove()) { 
    498509                parent.remove_from_parent(); 
     
    791802            } 
    792803        } 
    793         if (level.children != null) { 
    794             for (QBLevel child:level.children) { 
    795                 printTreeRecursive(child, indent + 2); 
    796             } 
     804        for (QBLevel child:level.getChildren()) { 
     805            printTreeRecursive(child, indent + 2); 
    797806        } 
    798807    } 
  • trunk/test/unit/org/openstreetmap/josm/data/osm/QuadBucketsTest.java

    r3166 r3476  
    44import java.io.FileInputStream; 
    55import java.util.ArrayList; 
     6import java.util.Collection; 
     7import java.util.Iterator; 
    68import java.util.List; 
    79 
     10import org.fest.reflect.core.Reflection; 
     11import org.fest.reflect.reference.TypeRef; 
    812import org.junit.Assert; 
    913import org.junit.Test; 
     
    2024        List<Way> allWays = new ArrayList<Way>(ds.getWays()); 
    2125        List<Relation> allRelations = new ArrayList<Relation>(ds.getRelations()); 
     26 
     27        QuadBuckets<Node> nodes = Reflection.field("nodes").ofType(new TypeRef<QuadBuckets<Node>>() {}).in(ds).get(); 
     28        QuadBuckets<Way> ways = Reflection.field("ways").ofType(new TypeRef<QuadBuckets<Way>>() {}).in(ds).get(); 
     29        Collection<Relation> relations = Reflection.field("relations").ofType(new TypeRef<Collection<Relation>>() {}).in(ds).get(); 
     30 
     31        int expectedCount = allNodes.size(); 
    2232        for (OsmPrimitive o: allNodes) { 
    2333            ds.removePrimitive(o); 
     34            checkIterator(nodes, --expectedCount); 
    2435        } 
     36        expectedCount = allWays.size(); 
    2537        for (OsmPrimitive o: allWays) { 
    2638            ds.removePrimitive(o); 
     39            checkIterator(ways, --expectedCount); 
    2740        } 
    2841        for (OsmPrimitive o: allRelations) { 
    2942            ds.removePrimitive(o); 
    3043        } 
     44        Assert.assertTrue(nodes.isEmpty()); 
     45        Assert.assertTrue(ways.isEmpty()); 
     46        Assert.assertTrue(relations.isEmpty()); 
     47    } 
    3148 
    32         Assert.assertTrue(ds.getNodes().isEmpty()); 
    33         Assert.assertTrue(ds.getWays().isEmpty()); 
    34         Assert.assertTrue(ds.getRelations().isEmpty()); 
     49    private void checkIterator(Collection<? extends OsmPrimitive> col, int expectedCount) { 
     50        int count = 0; 
     51        Iterator<? extends OsmPrimitive> it = col.iterator(); 
     52        while (it.hasNext()) { 
     53            count++; 
     54            it.next(); 
     55        } 
     56        Assert.assertEquals(expectedCount, count); 
    3557    } 
    3658 
Note: See TracChangeset for help on using the changeset viewer.