Internal changes
The NamePool (used to allocate integer codes to the names of elements and attributes, for fast comparison) has been redesigned to improve scaleability. The new design has less contention when being updated by multiple threads in parallel, and performance degrades less as the number of names in the pool increases, though the absolute capacity is unchanged at 2^20 names.
Use of integer namecodes and fingerprints on the PullProvider interface (supporting Saxon's pull pipeline) has been dropped, further reducing unnecessary use of the NamePool and thus reducing potential for contention in applications that use pull processing.
The LinkedTree
structure (used for schemas and stylesheets and also
available for source documents) now holds element and attribute names as NodeName objects rather than integer name
codes. Name codes will be allocated lazily within the NodeName
objects if
required, but not otherwise. This is all part of a trend to reduce dependence on the
shared NamePool
.
The multi-threading on xsl:for-each
has been rewritten to reduce contention.
In one test, elapsed time reduced from 240 seconds to 40 seconds, but at the cost of a
memory increase from 80Mb to 650Mb. The threads implementing the body of the
xsl:for-each
instruction now accumulate their results in memory and
pass them over to the master thread in a single go, rather than passing them
incrementally one item at a time.
A new optimization has been introduced to reorder predicates within an expression or
pattern based on a rough estimate of the cost of evaluation. This affects filter
expressions with more than one (non-positional) predicate, such as
A[following::X][@Y]
(where the aim is to evaluate the cheapest
predicate first), and match patterns of the form A[P]/B[Q]
(where in effect
parent::A[P]
is taken as an additional predicate that can be evaluated
before or after evaluating [Q]
). The cost metrics are very simple, and have
no knowledge of data volumes or distribution, but they are adequate to distinguish a
very expensive predicate from a very cheap one, and in some cases this can make a huge
difference. (Note: ideally the calculation should also assess the probability of the
predicate returning true, but we make no attempt to do this.)