Changeset 3476 in josm


Ignore:
Timestamp:
2010-08-29T12:58:38+02:00 (14 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.