Ticket #11382: mapcss-use-bitmap-and-keycache-size.2.patch

File mapcss-use-bitmap-and-keycache-size.2.patch, 12.4 KB (added by michael2402, 10 years ago)
  • src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java

    diff --git a/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java b/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
    index 6de6336..9d33cf1 100644
    a b import java.lang.reflect.Field;  
    1212import java.nio.charset.StandardCharsets;
    1313import java.text.MessageFormat;
    1414import java.util.ArrayList;
    15 import java.util.BitSet;
    1615import java.util.Collections;
    1716import java.util.HashMap;
    1817import java.util.HashSet;
    public class MapCSSStyleSource extends StyleSource {  
    138137     */
    139138    public static class MapCSSRuleIndex {
    140139        /**
    141          * This is an iterator over all rules that are marked as possible in the bitset.
     140         * This is a subset of the MapCSS rules. It is represented by storing the indexes in the rules array. This allows for fast merging.
     141         *
     142         * It alternatively uses a list of set indexes, to ensure O(<number of set bits>) space requirement.
     143         *
     144         * @author Michael Zangl
     145         */
     146        private final static class RuleBitset {
     147            /**
     148             * A (sorted) array of rule indexes. Those indexes are the allowed rules. We do not use an {@link ArrayList} here because this would require us to box all integers (slow).
     149             */
     150            private int[] ruleIndexes = new int[5];
     151
     152            /**
     153             * Real length of {@link #ruleIndexes}
     154             */
     155            private int ruleIndexesLength = 0;
     156
     157            /**
     158             * The bitset data. We always store whole words. If it is <code>null</code>, this is not used.
     159             */
     160            private long[] bitsetData;
     161            /**
     162             * The number of words after which bitsetData starts.
     163             */
     164            private int bitsetOffset;
     165
     166            private void addRule(int ruleIndex) {
     167                if (bitsetData != null) {
     168                    throw new IllegalStateException("Cannot add to optimized bitset.");
     169                }
     170                if (ruleIndexesLength > 0 && ruleIndexes[ruleIndexesLength - 1] >= ruleIndex) {
     171                    throw new IllegalArgumentException("Rules must be sorted.");
     172                }
     173
     174                if (ruleIndexesLength >= ruleIndexes.length) {
     175                    int[] newIndexes = new int[ruleIndexes.length * 2];
     176                    System.arraycopy(ruleIndexes, 0, newIndexes, 0, ruleIndexes.length);
     177                    ruleIndexes = newIndexes;
     178                }
     179
     180                ruleIndexes[ruleIndexesLength] = ruleIndex;
     181                ruleIndexesLength++;
     182            }
     183
     184            /**
     185             * Optimize this list by converting it to a bitset (at least partially)
     186             */
     187            private void optimize() {
     188                if (ruleIndexesLength == 0) {
     189                    return;
     190                }
     191
     192                int minRule = ruleIndexes[0];
     193                int maxRule = ruleIndexes[ruleIndexesLength - 1];
     194
     195                // current policy: completely convert to bitset if on average 1 bit per word is set.
     196                // alternatives: convert one dense area, ...
     197                int usedWords = getWord(maxRule) - getWord(minRule) + 1;
     198                if (usedWords <= ruleIndexesLength - 1) {
     199                    bitsetOffset = getWord(minRule);
     200                    bitsetData = new long[usedWords];
     201                    for (int i = 0; i < ruleIndexesLength; i++) {
     202                        int ruleIndex = ruleIndexes[i];
     203                        bitsetData[getWord(ruleIndex) - bitsetOffset] |= getMask(ruleIndex);
     204                    }
     205                    ruleIndexes = null;
     206                    ruleIndexesLength = 0;
     207                }
     208            }
     209
     210            private static long getMask(int bit) {
     211                return (1l << (bit & 63));
     212            }
     213
     214            private static int getWord(int bit) {
     215                return bit / 64;
     216            }
     217
     218            public void setBitsIn(long[] ruleCandidateBits) {
     219                if (bitsetData != null) {
     220                    for (int i = 0 ; i < bitsetData.length; i++) {
     221                        ruleCandidateBits[bitsetOffset + i] |= bitsetData[i] ;
     222                    }
     223                }
     224
     225                for (int i = 0; i < ruleIndexesLength; i++) {
     226                    int bitIndex = ruleIndexes[i];
     227                    ruleCandidateBits[getWord(bitIndex)] |= getMask(bitIndex);
     228                }
     229            }
     230        }
     231
     232        /**
     233         * This is an iterator over all rules that are marked as possible in the bitsets. Rules can be added to it before it is used.
    142234         *
    143235         * @author Michael Zangl
    144236         */
    145237        private final class RuleCandidatesIterator implements Iterator<MapCSSRule> {
    146             private final BitSet ruleCandidates;
     238            /**
     239             * The rules that this iterator should iterate over.
     240             * <p>
     241             * We cannot use a bitset here, because it does not expose it's long[]
     242             */
     243            private final long[] ruleCandidateBits;
    147244            private int next;
    148245
    149             private RuleCandidatesIterator(BitSet ruleCandidates) {
    150                 this.ruleCandidates = ruleCandidates;
    151                 next = ruleCandidates.nextSetBit(0);
     246            private RuleCandidatesIterator(long[] ruleCandidateBits) {
     247                this.ruleCandidateBits = ruleCandidateBits;
     248                next = nextSetBit(0);
     249            }
     250
     251            private int nextSetBit(int from) {
     252                int wordIndex = RuleBitset.getWord(from);
     253                if (wordIndex == ruleCandidateBits.length) {
     254                    return -1;
     255                }
     256
     257                long nextWord = ruleCandidateBits[wordIndex] & (-1l << from);
     258
     259                while (true) {
     260                    if (nextWord != 0)
     261                        return (wordIndex * 64) + Long.numberOfTrailingZeros(nextWord);
     262
     263                    wordIndex++;
     264                    if (wordIndex == ruleCandidateBits.length)
     265                        return -1;
     266                    nextWord = ruleCandidateBits[wordIndex];
     267                }
    152268            }
    153269
    154270            @Override
    public class MapCSSStyleSource extends StyleSource {  
    159275            @Override
    160276            public MapCSSRule next() {
    161277                MapCSSRule rule = rules.get(next);
    162                 next = ruleCandidates.nextSetBit(next + 1);
     278                next = nextSetBit(next + 1);
    163279                return rule;
    164280            }
    165281
    public class MapCSSStyleSource extends StyleSource {  
    167283            public void remove() {
    168284                throw new UnsupportedOperationException();
    169285            }
     286
     287            @Override
     288            public String toString() {
     289                StringBuilder builder = new StringBuilder();
     290                builder.append("RuleCandidatesIterator [ruleCandidateBits=[");
     291                boolean needComma = false;
     292                for (int i = nextSetBit(0); i >= 0; i = nextSetBit(i + 1)) {
     293                    if (needComma) {
     294                        builder.append(", ");
     295                    }
     296                    needComma = true;
     297                    builder.append(i);
     298                }
     299                builder.append("], next=");
     300                builder.append(next);
     301                builder.append("]");
     302                return builder.toString();
     303            }
    170304        }
    171305
    172306        /**
    public class MapCSSStyleSource extends StyleSource {  
    178312            /**
    179313             * The indexes of rules that might be applied if this tag is present and the value has no special handling.
    180314             */
    181             BitSet generalRules = new BitSet();
     315            RuleBitset generalRules = new RuleBitset();
    182316
    183317            /**
    184318             * A map that sores the indexes of rules that might be applied if the key=value pair is present on this
    185319             * primitive. This includes all key=* rules.
    186320             */
    187             Map<String, BitSet> specialRules = new HashMap<>();
     321            HashMap<String, RuleBitset> specialRules = new HashMap<>();
    188322
    189323            public void addForKey(int ruleIndex) {
    190                 generalRules.set(ruleIndex);
    191                 for (BitSet r : specialRules.values()) {
    192                     r.set(ruleIndex);
    193                 }
     324                generalRules.addRule(ruleIndex);
    194325            }
    195326
    196327            public void addForKeyAndValue(String value, int ruleIndex) {
    197                 BitSet forValue = specialRules.get(value);
     328                RuleBitset forValue = specialRules.get(value);
    198329                if (forValue == null) {
    199                     forValue = new BitSet();
    200                     forValue.or(generalRules);
     330                    forValue = new RuleBitset();
    201331                    specialRules.put(value.intern(), forValue);
    202332                }
    203                 forValue.set(ruleIndex);
     333                forValue.addRule(ruleIndex);
    204334            }
    205335
    206             public BitSet get(String value) {
    207                 BitSet forValue = specialRules.get(value);
    208                 if (forValue != null) return forValue; else return generalRules;
     336            public void optimize() {
     337                generalRules.optimize();
     338                for (RuleBitset bitset : specialRules.values()) {
     339                    bitset.optimize();
     340                }
     341            }
     342
     343            public void setBitsIn(long[] ruleCandidateBits, String value) {
     344                RuleBitset forValue = specialRules.get(value);
     345                if (forValue != null)
     346                    forValue.setBitsIn(ruleCandidateBits);
     347                generalRules.setBitsIn(ruleCandidateBits);
    209348            }
    210349        }
    211350
    public class MapCSSStyleSource extends StyleSource {  
    220359        /**
    221360         * Rules that do not require any key to be present. Only the index in the {@link #rules} array is stored.
    222361         */
    223         private final BitSet remaining = new BitSet();
     362        private RuleBitset remaining = new RuleBitset();
    224363
    225364        /**
    226365         * Add a rule to this index. This needs to be called before {@link #initIndex()} is called.
    public class MapCSSStyleSource extends StyleSource {  
    246385                }
    247386                OptimizedGeneralSelector s = (OptimizedGeneralSelector) selRightmost;
    248387                if (s.conds == null) {
    249                     remaining.set(ruleIndex);
     388                    remaining.addRule(ruleIndex);
    250389                    continue;
    251390                }
    252391                List<SimpleKeyValueCondition> sk = new ArrayList<>(Utils.filteredCollection(s.conds,
    public class MapCSSStyleSource extends StyleSource {  
    259398                    if (key != null) {
    260399                        getEntryInIndex(key).addForKey(ruleIndex);
    261400                    } else {
    262                         remaining.set(ruleIndex);
     401                        remaining.addRule(ruleIndex);
    263402                    }
    264403                }
    265404            }
     405
     406            remaining.optimize();
     407            for (MapCSSKeyRules rules : index.values()) {
     408                rules.optimize();
     409            }
    266410        }
    267411
    268412        /**
    public class MapCSSStyleSource extends StyleSource {  
    312456         * @return An iterator over possible rules in the right order.
    313457         */
    314458        public Iterator<MapCSSRule> getRuleCandidates(OsmPrimitive osm) {
    315             final BitSet ruleCandidates = new BitSet(rules.size());
    316             ruleCandidates.or(remaining);
     459            long[] ruleCandidateBits = new long[RuleBitset.getWord(rules.size() - 1) + 1];
     460            remaining.setBitsIn(ruleCandidateBits);
    317461
    318462            for (Map.Entry<String, String> e : osm.getKeys().entrySet()) {
    319463                MapCSSKeyRules v = index.get(e.getKey());
    320464                if (v != null) {
    321                     BitSet rs = v.get(e.getValue());
    322                     ruleCandidates.or(rs);
     465                    v.setBitsIn(ruleCandidateBits, e.getValue());
    323466                }
    324467            }
    325             return new RuleCandidatesIterator(ruleCandidates);
     468            return new RuleCandidatesIterator(ruleCandidateBits);
    326469        }
    327470
    328471        /**
    public class MapCSSStyleSource extends StyleSource {  
    333476        public void clear() {
    334477            rules.clear();
    335478            index.clear();
    336             remaining.clear();
     479            remaining = new RuleBitset();
    337480        }
    338481    }
    339482