Changeset 19285 in josm


Ignore:
Timestamp:
2025-01-16T18:42:37+01:00 (5 weeks ago)
Author:
taylor.smock
Message:

Fix #24075: Reduce memory allocations for TaggingPresetItem#matches

This is done by doing the following:

  • Converting KeyedItem.match to a MatchType from a String
    • This avoids calling MatchType#ofString repeatedly
    • This does decrease the visibility of the match field and change the type
  • Avoiding ArrayList.Itr creation in TaggingPresetItem#matches
    • This does produce some duplicate code, unfortunately.

The KeyedItem.match change reduces memory allocations in KeyedItem#matches by
98% and CPU cycles by 77%.
The TaggingPresetItem#matches change to avoid ArrayList.Itr creation reduces
memory allocations by 100% and CPU cycles by 94% for ArrayList (only looking at
changes between the for loop types).

The net change for TaggingPresetItem#matches is a reduction of memory
allocations by 99% and CPU cycles by 74%. As noted in the ticket, there was a
reduction in GC by ~80%.

Location:
trunk/src/org/openstreetmap/josm
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java

    r19201 r19285  
    421421                for (TaggingPresetItem i : p.data) {
    422422                    if (i instanceof KeyedItem) {
    423                         if (!"none".equals(((KeyedItem) i).match))
     423                        if (!"none".equals(((KeyedItem) i).match()))
    424424                            minData.add(i);
    425425                        addPresetValue((KeyedItem) i);
    426426                    } else if (i instanceof CheckGroup) {
    427427                        for (Check c : ((CheckGroup) i).checks) {
    428                             if (!"none".equals(c.match))
     428                            if (!"none".equals(c.match()))
    429429                                minData.add(c);
    430430                            addPresetValue(c);
  • trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetItem.java

    r18258 r19285  
    1212import java.util.List;
    1313import java.util.Map;
     14import java.util.RandomAccess;
    1415import java.util.Set;
    1516
     
    171172    public static boolean matches(Iterable<? extends TaggingPresetItem> data, Map<String, String> tags) {
    172173        boolean atLeastOnePositiveMatch = false;
    173         for (TaggingPresetItem item : data) {
    174             Boolean m = item.matches(tags);
    175             if (m != null && !m)
    176                 return false;
    177             else if (m != null) {
    178                 atLeastOnePositiveMatch = true;
     174        if (data instanceof List && data instanceof RandomAccess) {
     175            List<? extends TaggingPresetItem> items = (List<? extends TaggingPresetItem>) data;
     176            /* This is a memory allocation optimization, mostly for ArrayList.
     177             * In test runs, this reduced the memory cost for this method by 99%.
     178             * This appears to have also improved CPU cost for this method by ~10% as well.
     179             * The big win for CPU cost is in GC improvements, which was around 80%.
     180             * Overall improvement: 7.6 hours to 4.5 hours for validating a Colorado pbf extract (40% improvement).
     181             */
     182            for (int i = 0; i < items.size(); i++) { // READ ABOVE: DO NOT REPLACE WITH ENHANCED FOR LOOP WITHOUT PROFILING!
     183                TaggingPresetItem item = items.get(i);
     184                Boolean m = item.matches(tags);
     185                if (m != null && !m) {
     186                    return false;
     187                } else if (m != null) {
     188                    atLeastOnePositiveMatch = true;
     189                }
    179190            }
     191        } else {
     192            for (TaggingPresetItem item : data) {
     193                Boolean m = item.matches(tags);
     194                if (m != null && !m) {
     195                    return false;
     196                } else if (m != null) {
     197                    atLeastOnePositiveMatch = true;
     198                }
     199            }
    180200        }
    181201        return atLeastOnePositiveMatch;
  • trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/KeyedItem.java

    r18918 r19285  
    5252     * Default is "keyvalue!" for {@link Key} and "none" for {@link Text}, {@link Combo}, {@link MultiSelect} and {@link Check}.
    5353     */
    54     public String match = getDefaultMatch().getValue(); // NOSONAR
     54    protected MatchType match = getDefaultMatch(); // NOSONAR
    5555
    5656    /**
     
    180180            return usage;
    181181        }
     182    }
     183
     184    /**
     185     * Allows to change the matching process, i.e., determining whether the tags of an OSM object fit into this preset.
     186     * If a preset fits then it is linked in the Tags/Membership dialog.<ul>
     187     * <li>none: neutral, i.e., do not consider this item for matching</li>
     188     * <li>key: positive if key matches, neutral otherwise</li>
     189     * <li>key!: positive if key matches, negative otherwise</li>
     190     * <li>keyvalue: positive if key and value matches, neutral otherwise</li>
     191     * <li>keyvalue!: positive if key and value matches, negative otherwise</li></ul>
     192     * Note that for a match, at least one positive and no negative is required.
     193     * Default is "keyvalue!" for {@link Key} and "none" for {@link Text}, {@link Combo}, {@link MultiSelect} and {@link Check}.
     194     * @param match The match type. One of <code>none</code>, <code>key</code>, <code>key!</code>, <code>keyvalue</code>,
     195     *              or <code>keyvalue!</code>.
     196     * @since 19285
     197     */
     198    public void setMatch(String match) {
     199        this.match = MatchType.ofString(match);
     200    }
     201
     202    /**
     203     * Get the match type for this item
     204     * @return The match type
     205     * @since 19285
     206     */
     207    public String match() {
     208        return this.match.getValue();
    182209    }
    183210
     
    222249     */
    223250    public boolean isKeyRequired() {
    224         final MatchType type = MatchType.ofString(match);
     251        final MatchType type = this.match;
    225252        return MatchType.KEY_REQUIRED == type || MatchType.KEY_VALUE_REQUIRED == type;
    226253    }
     
    244271    @Override
    245272    public Boolean matches(Map<String, String> tags) {
    246         switch (MatchType.ofString(match)) {
     273        switch (this.match) {
    247274        case NONE:
    248275            return null; // NOSONAR
Note: See TracChangeset for help on using the changeset viewer.