Ticket #11382: mapcss-use-bitmap-and-keycache-size.2.patch
File mapcss-use-bitmap-and-keycache-size.2.patch, 12.4 KB (added by , 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; 12 12 import java.nio.charset.StandardCharsets; 13 13 import java.text.MessageFormat; 14 14 import java.util.ArrayList; 15 import java.util.BitSet;16 15 import java.util.Collections; 17 16 import java.util.HashMap; 18 17 import java.util.HashSet; … … public class MapCSSStyleSource extends StyleSource { 138 137 */ 139 138 public static class MapCSSRuleIndex { 140 139 /** 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. 142 234 * 143 235 * @author Michael Zangl 144 236 */ 145 237 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; 147 244 private int next; 148 245 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 } 152 268 } 153 269 154 270 @Override … … public class MapCSSStyleSource extends StyleSource { 159 275 @Override 160 276 public MapCSSRule next() { 161 277 MapCSSRule rule = rules.get(next); 162 next = ruleCandidates.nextSetBit(next + 1);278 next = nextSetBit(next + 1); 163 279 return rule; 164 280 } 165 281 … … public class MapCSSStyleSource extends StyleSource { 167 283 public void remove() { 168 284 throw new UnsupportedOperationException(); 169 285 } 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 } 170 304 } 171 305 172 306 /** … … public class MapCSSStyleSource extends StyleSource { 178 312 /** 179 313 * The indexes of rules that might be applied if this tag is present and the value has no special handling. 180 314 */ 181 BitSet generalRules = new BitSet();315 RuleBitset generalRules = new RuleBitset(); 182 316 183 317 /** 184 318 * A map that sores the indexes of rules that might be applied if the key=value pair is present on this 185 319 * primitive. This includes all key=* rules. 186 320 */ 187 Map<String, BitSet> specialRules = new HashMap<>();321 HashMap<String, RuleBitset> specialRules = new HashMap<>(); 188 322 189 323 public void addForKey(int ruleIndex) { 190 generalRules.set(ruleIndex); 191 for (BitSet r : specialRules.values()) { 192 r.set(ruleIndex); 193 } 324 generalRules.addRule(ruleIndex); 194 325 } 195 326 196 327 public void addForKeyAndValue(String value, int ruleIndex) { 197 BitSet forValue = specialRules.get(value);328 RuleBitset forValue = specialRules.get(value); 198 329 if (forValue == null) { 199 forValue = new BitSet(); 200 forValue.or(generalRules); 330 forValue = new RuleBitset(); 201 331 specialRules.put(value.intern(), forValue); 202 332 } 203 forValue. set(ruleIndex);333 forValue.addRule(ruleIndex); 204 334 } 205 335 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); 209 348 } 210 349 } 211 350 … … public class MapCSSStyleSource extends StyleSource { 220 359 /** 221 360 * Rules that do not require any key to be present. Only the index in the {@link #rules} array is stored. 222 361 */ 223 private final BitSet remaining = new BitSet();362 private RuleBitset remaining = new RuleBitset(); 224 363 225 364 /** 226 365 * Add a rule to this index. This needs to be called before {@link #initIndex()} is called. … … public class MapCSSStyleSource extends StyleSource { 246 385 } 247 386 OptimizedGeneralSelector s = (OptimizedGeneralSelector) selRightmost; 248 387 if (s.conds == null) { 249 remaining. set(ruleIndex);388 remaining.addRule(ruleIndex); 250 389 continue; 251 390 } 252 391 List<SimpleKeyValueCondition> sk = new ArrayList<>(Utils.filteredCollection(s.conds, … … public class MapCSSStyleSource extends StyleSource { 259 398 if (key != null) { 260 399 getEntryInIndex(key).addForKey(ruleIndex); 261 400 } else { 262 remaining. set(ruleIndex);401 remaining.addRule(ruleIndex); 263 402 } 264 403 } 265 404 } 405 406 remaining.optimize(); 407 for (MapCSSKeyRules rules : index.values()) { 408 rules.optimize(); 409 } 266 410 } 267 411 268 412 /** … … public class MapCSSStyleSource extends StyleSource { 312 456 * @return An iterator over possible rules in the right order. 313 457 */ 314 458 public Iterator<MapCSSRule> getRuleCandidates(OsmPrimitive osm) { 315 final BitSet ruleCandidates = new BitSet(rules.size());316 r uleCandidates.or(remaining);459 long[] ruleCandidateBits = new long[RuleBitset.getWord(rules.size() - 1) + 1]; 460 remaining.setBitsIn(ruleCandidateBits); 317 461 318 462 for (Map.Entry<String, String> e : osm.getKeys().entrySet()) { 319 463 MapCSSKeyRules v = index.get(e.getKey()); 320 464 if (v != null) { 321 BitSet rs = v.get(e.getValue()); 322 ruleCandidates.or(rs); 465 v.setBitsIn(ruleCandidateBits, e.getValue()); 323 466 } 324 467 } 325 return new RuleCandidatesIterator(ruleCandidate s);468 return new RuleCandidatesIterator(ruleCandidateBits); 326 469 } 327 470 328 471 /** … … public class MapCSSStyleSource extends StyleSource { 333 476 public void clear() { 334 477 rules.clear(); 335 478 index.clear(); 336 remaining .clear();479 remaining = new RuleBitset(); 337 480 } 338 481 } 339 482