Ticket #11382: mapcss-use-bitmap-and-keycache-size.patch
File mapcss-use-bitmap-and-keycache-size.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..80f8449 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 - 2) { 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 219 /** 220 * 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 221 * 143 222 * @author Michael Zangl 144 223 */ 145 224 private final class RuleCandidatesIterator implements Iterator<MapCSSRule> { 146 private final BitSet ruleCandidates; 225 /** 226 * The rules that this iterator should iterate over. 227 * <p> 228 * We cannot use a bitset here, because it does not expose it's long[] 229 */ 230 private final long[] ruleCandidateBits; 147 231 private int next; 148 232 149 private RuleCandidatesIterator(BitSet ruleCandidates) { 150 this.ruleCandidates = ruleCandidates; 151 next = ruleCandidates.nextSetBit(0); 233 private RuleCandidatesIterator() { 234 ruleCandidateBits = new long[RuleBitset.getWord(rules.size() - 1) + 1]; 235 } 236 237 private int nextSetBit(int from) { 238 int wordIndex = RuleBitset.getWord(from); 239 if (wordIndex == ruleCandidateBits.length) { 240 return -1; 241 } 242 243 long nextWord = ruleCandidateBits[wordIndex] & (-1l << from); 244 245 while (true) { 246 if (nextWord != 0) 247 return (wordIndex * 64) + Long.numberOfTrailingZeros(nextWord); 248 249 wordIndex++; 250 if (wordIndex == ruleCandidateBits.length) 251 return -1; 252 nextWord = ruleCandidateBits[wordIndex]; 253 } 254 } 255 256 public void or(RuleBitset set) { 257 if (set.bitsetData != null) { 258 for (int i = 0 ; i < set.bitsetData.length; i++) { 259 ruleCandidateBits[set.bitsetOffset + i] |= set.bitsetData[i] ; 260 } 261 } 262 263 for (int i = 0; i < set.ruleIndexesLength; i++) { 264 setBit(set.ruleIndexes[i]); 265 } 266 } 267 268 private void setBit(int bitIndex) { 269 ruleCandidateBits[RuleBitset.getWord(bitIndex)] |= RuleBitset.getMask(bitIndex); 270 } 271 272 public void prepare() { 273 next = nextSetBit(0); 152 274 } 153 275 154 276 @Override … … public class MapCSSStyleSource extends StyleSource { 159 281 @Override 160 282 public MapCSSRule next() { 161 283 MapCSSRule rule = rules.get(next); 162 next = ruleCandidates.nextSetBit(next + 1);284 next = nextSetBit(next + 1); 163 285 return rule; 164 286 } 165 287 … … public class MapCSSStyleSource extends StyleSource { 167 289 public void remove() { 168 290 throw new UnsupportedOperationException(); 169 291 } 292 293 @Override 294 public String toString() { 295 StringBuilder builder = new StringBuilder(); 296 builder.append("RuleCandidatesIterator [ruleCandidateBits=["); 297 boolean needComma = false; 298 for (int i = nextSetBit(0); i >= 0; i = nextSetBit(i + 1)) { 299 if (needComma) { 300 builder.append(", "); 301 } 302 needComma = true; 303 builder.append(i); 304 } 305 builder.append("], next="); 306 builder.append(next); 307 builder.append("]"); 308 return builder.toString(); 309 } 310 311 170 312 } 171 313 172 314 /** … … public class MapCSSStyleSource extends StyleSource { 178 320 /** 179 321 * The indexes of rules that might be applied if this tag is present and the value has no special handling. 180 322 */ 181 BitSet generalRules = new BitSet();323 RuleBitset generalRules = new RuleBitset(); 182 324 183 325 /** 184 326 * A map that sores the indexes of rules that might be applied if the key=value pair is present on this 185 327 * primitive. This includes all key=* rules. 186 328 */ 187 Map<String, BitSet> specialRules = new HashMap<>();329 HashMap<String, RuleBitset> specialRules = new HashMap<>(); 188 330 189 331 public void addForKey(int ruleIndex) { 190 generalRules.set(ruleIndex); 191 for (BitSet r : specialRules.values()) { 192 r.set(ruleIndex); 193 } 332 generalRules.addRule(ruleIndex); 194 333 } 195 334 196 335 public void addForKeyAndValue(String value, int ruleIndex) { 197 BitSet forValue = specialRules.get(value);336 RuleBitset forValue = specialRules.get(value); 198 337 if (forValue == null) { 199 forValue = new BitSet(); 200 forValue.or(generalRules); 338 forValue = new RuleBitset(); 201 339 specialRules.put(value.intern(), forValue); 202 340 } 203 forValue.set(ruleIndex); 341 forValue.addRule(ruleIndex); 342 } 343 344 public void get(RuleCandidatesIterator i, String value) { 345 RuleBitset forValue = specialRules.get(value); 346 if (forValue != null) 347 i.or(forValue); 348 i.or(generalRules); 204 349 } 205 350 206 public BitSet get(String value) { 207 BitSet forValue = specialRules.get(value); 208 if (forValue != null) return forValue; else return generalRules; 351 public void optimize() { 352 generalRules.optimize(); 353 for (RuleBitset bitset : specialRules.values()) { 354 bitset.optimize(); 355 } 209 356 } 210 357 } 211 358 … … public class MapCSSStyleSource extends StyleSource { 220 367 /** 221 368 * Rules that do not require any key to be present. Only the index in the {@link #rules} array is stored. 222 369 */ 223 private final BitSet remaining = new BitSet();370 private RuleBitset remaining = new RuleBitset(); 224 371 225 372 /** 226 373 * Add a rule to this index. This needs to be called before {@link #initIndex()} is called. … … public class MapCSSStyleSource extends StyleSource { 246 393 } 247 394 OptimizedGeneralSelector s = (OptimizedGeneralSelector) selRightmost; 248 395 if (s.conds == null) { 249 remaining. set(ruleIndex);396 remaining.addRule(ruleIndex); 250 397 continue; 251 398 } 252 399 List<SimpleKeyValueCondition> sk = new ArrayList<>(Utils.filteredCollection(s.conds, … … public class MapCSSStyleSource extends StyleSource { 259 406 if (key != null) { 260 407 getEntryInIndex(key).addForKey(ruleIndex); 261 408 } else { 262 remaining. set(ruleIndex);409 remaining.addRule(ruleIndex); 263 410 } 264 411 } 265 412 } 413 414 remaining.optimize(); 415 for (MapCSSKeyRules rules : index.values()) { 416 rules.optimize(); 417 } 266 418 } 267 419 268 420 /** … … public class MapCSSStyleSource extends StyleSource { 312 464 * @return An iterator over possible rules in the right order. 313 465 */ 314 466 public Iterator<MapCSSRule> getRuleCandidates(OsmPrimitive osm) { 315 final BitSet ruleCandidates = new BitSet(rules.size());467 final RuleCandidatesIterator ruleCandidates = new RuleCandidatesIterator(); 316 468 ruleCandidates.or(remaining); 317 469 318 470 for (Map.Entry<String, String> e : osm.getKeys().entrySet()) { 319 471 MapCSSKeyRules v = index.get(e.getKey()); 320 472 if (v != null) { 321 BitSet rs = v.get(e.getValue()); 322 ruleCandidates.or(rs); 473 v.get(ruleCandidates, e.getValue()); 323 474 } 324 475 } 325 return new RuleCandidatesIterator(ruleCandidates); 476 ruleCandidates.prepare(); 477 return ruleCandidates; 326 478 } 327 479 328 480 /** … … public class MapCSSStyleSource extends StyleSource { 333 485 public void clear() { 334 486 rules.clear(); 335 487 index.clear(); 336 remaining .clear();488 remaining = new RuleBitset(); 337 489 } 338 490 } 339 491