Project

Profile

Help

Saxon Performance

Added by Vladimir Nesterovsky about 2 years ago

While looking at issue Gradual degradation of load performance across Saxon engine versions I feel the need to express an idea that in my opinion will increase the performance.

I suggest to replace NamePool class and use of fingerprints to identify names and qnames with VM-wide weak pool of names and qnames.

The virtue of this technique is that names and qnames are identity comparable, which makes comparison as fast as of integers. At the same time qname instances are cheaper than fingerprints if you need to access their components.

VM-wide weak pool allows to:

  • share data between concurrent transformations;
  • reduces GC pressure comparing to NamePool;
  • automatically reclaims unused instances.

Please look at sample implementation:

In addition, direct use of names and qnames may greatly simplify code.


Replies (6)

Please register to reply

RE: Saxon Performance - Added by Michael Kay about 2 years ago

It's an interesting idea and worth experimenting with. In the past I've tried to avoid having any objects shared VM-wide, but perhaps those anxieties are no longer well-founded. And the use of ConcurrentHashMap in the current "Mk 2" namepool design certainly seems to have eliminated the contention problems, so I'm not so worried about that.

Eliminating the dependence on comparable names being in the same Configuration/NamePool would certainly be a valuable simplification, both internally and for users.

I think there are semantics concerned with prefixes that you haven't quite captured in your sample implementation, but I don't think that poses any insuperable problems.

I've wondered occasionally about using interned strings for namespace URIs, I don't know if you have any views on that, in particular how interning compares with your WeakPool, especially when (as with namespace URIs) the population of strings is likely to be small and stable. This reference [ http://java-performance.info/string-intern-in-java-6-7-8/ ] suggests that for Java 8, string interning should be considered.

Another factor for representing QName is size. Saxon's StructuredQName object contains a char[] object and three int's. That's a lot less overhead than having three String objects in the naive implementation (as in javax.xml.namespace.QName); it's designed to get a balance between size, comparison cost, and the cost of extracting components of the name.

Also, we hold names as int values in the TinyTree; that's 4 bytes, whereas an object reference would typically be 8 bytes.

RE: Saxon Performance - Added by Vladimir Nesterovsky about 2 years ago

String interning will probably be faster and will consume less memory. The only drawback is that it never releases memory back. That is why I don't like it for use of unknown number of strings.

we hold names as int values in the TinyTree; that's 4 bytes, whereas an object reference would typically be 8 bytes.

That is part of trade of memory for performance: int is smaller but requires dereferencing logic.

On the other hand TinyTree can store array of qname instances, so those int values can be indices of that array.

RE: Saxon Performance - Added by Vladimir Nesterovsky about 2 years ago

In fact http://java-performance.info/string-intern-in-java-6-7-8/ tells that interned strings are reclaimed by GC, so they might be a pulled like this. Perhaps with some configuration option.

RE: Saxon Performance - Added by Vladimir Nesterovsky about 2 years ago

On the other hand https://shipilev.net/jvm/anatomy-quarks/10-string-intern/ repeats arguments of thr previous article but concludes that manual pooling is more flexible.

RE: Saxon Performance - Added by Vladimir Nesterovsky about 2 years ago

StructuredQName object contains a char[] object and three int's.

In Java >= 10 string data is stored in byte[]:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

    /**
     * The value is used for character storage.
     */
    private final byte[] value;

    /**
     * The identifier of the encoding used to encode the bytes in
     * {@code value}. The supported values in this implementation are
     *
     * LATIN1
     * UTF16
     */
    private final byte coder;

    /** Cache the hash code for the string */
    private int hash; // Default to 0

So, I'm not so sure of advantage of char[], as in many case it will consume more space.

In addition pulled qname would reuse the same instances, including same instances of components, which would turn localname, namespace, or whole qname comparison into trivial identity comparision, and would allow IdentityHashMap to index such data.

RE: Saxon Performance - Added by Michael Kay about 2 years ago

Yes, thanks, I'm aware of these upcoming changes (I think they actually appear in Java 9). At the moment we're primarily targeting Java 8. Certainly these changes influence the design of classes such as StructuredQName where by using char[] we are failing to take advantage of the improvements the JDK has made to java.lang.String.

Saxon has its own family of classes under UnicodeString which represent a string using either byte[], char[], or int[] depending on the highest codepoint found in the string. This goes beyond space-saving, it's also designed to give fast addressing of Unicode codepoints for operations such as substring() and translate(), and for regular expression processing. The case for this still remains, I think. But we don't use these classes universally, and in particular we don't use them for QNames.

    (1-6/6)

    Please register to reply