Changeset 19079 in josm for trunk


Ignore:
Timestamp:
2024-05-14T19:29:53+02:00 (7 weeks ago)
Author:
taylor.smock
Message:

See #23671: Significantly improve performance of Utils.isBlank and Utils.isStripEmpty along with reducing intern cost when loading files

From Utils, isBlank and isStripEmpty are functionally similar; both take a
string and check for the same whitespace characters. Specifically, the code flow
for isBlank calls strip, which uses DEFAULT_STRIP as additional characters
to remove. strip eventually calls isStrippedChar to determine whether or not
to keep a character. isStrippedChar checks DEFAULT_STRIP always, so the
returns from Utils.isBlank and Utils.isStripEmpty should always be the same.

Since the two functions are the same, we change isBlank to point at
isStripEmpty, and isStripEmpty now calls String.isBlank prior to checking
every character. String.isBlank is a built-in method, and is thus more likely
to be well-optimized by the JVM. In the event that it returns false, we want to
avoid any additional work, so we use a traditional for loop instead of a stream;
we will usually know within a character or two whether or not the string is empty.
With a stream, we first have to construct the stream, then iterate through it.

In AbstractReader, we create a map of strings to interned strings to avoid
calling String.intern too often -- despite being something we want to do fairly
frequently. Part of the (current) problem with String.intern is that it uses a
ConcurrentHashTable, so it "costs" more to use. The HashMap (even with the
cost of the lambdas) is ~25% of the CPU cost of interning, given sufficiently
duplicated data.

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

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/io/AbstractReader.java

    r18697 r19079  
    8888
    8989    /**
     90     * A lookup table to avoid calling {@link String#intern()} unnecessarily.
     91     */
     92    private final Map<String, String> tagMap = new HashMap<>();
     93
     94    /**
    9095     * The dataset to add parsed objects to.
    9196     */
     
    370375                }
    371376            }
     377            this.tagMap.clear();
    372378            progressMonitor.finishTask();
    373379            progressMonitor.removeCancelListener(cancelListener);
     
    605611            ((AbstractPrimitive) t).setModified(true);
    606612        } else {
    607             t.put(key.intern(), value.intern());
     613            t.put(this.tagMap.computeIfAbsent(key, Utils::intern), this.tagMap.computeIfAbsent(value, Utils::intern));
    608614        }
    609615    }
  • trunk/src/org/openstreetmap/josm/tools/Utils.java

    r19050 r19079  
    739739     */
    740740    public static boolean isBlank(String string) {
    741         return string == null || strip(string).isEmpty();
     741        return isStripEmpty(string);
    742742    }
    743743
     
    763763     */
    764764    public static boolean isStripEmpty(String str) {
    765         return str == null || IntStream.range(0, str.length()).allMatch(i -> isStrippedChar(str.charAt(i), null));
     765        if (str != null && !str.isBlank()) {
     766            for (int i = 0; i < str.length(); i++) {
     767                if (!isStrippedChar(str.charAt(i), null)) {
     768                    return false;
     769                }
     770            }
     771        }
     772        return true;
    766773    }
    767774
Note: See TracChangeset for help on using the changeset viewer.