diff --git a/mozilla/extensions/transformiix/docs/optimized-stylesheets.html b/mozilla/extensions/transformiix/docs/optimized-stylesheets.html new file mode 100644 index 00000000000..7fa07b8ce1c --- /dev/null +++ b/mozilla/extensions/transformiix/docs/optimized-stylesheets.html @@ -0,0 +1,199 @@ + + + Optimizing Stylesheets + + + + +

Optimizing Stylesheets

+ +

Overview

+

+ This document outlines optimizations that we can perform on + stylesheets after a stylesheet is compiled, but before it is executed. +

+ + +

Create AVTs out of xsl:attributes

+

+ People often write stylesheets like +

+<LRE>
+  <xsl:attribute name="foo">
+    text here
+    <xsl:value-of select="@bar"/>
+  </xsl:attribute>
+
+ This could be optimized into an AVT like +
+<LRE foo="text here{@foo}">
+
+ This can be done both in templates and in attribute-sets +

+ + +

Investigate how RTFs are being used

+

+ When only the string-value or double-value of an RTF is being used it + is unneccesary to create an entire RTF. Instead we could set up a + string-handler and use the resulting string. +

+ +

+ Once we have a node-set() function we could also have a special + handler that creates that resulting nodeset rather then + whatever-we-create-once-we-have-real-RTFs. That handler would be set + up when we can determain that a RTF is used only as argument to the + node-set() function. +

+ +

+ In cases where an RTF is used as parameter in a call to another + template we could do further analysis to see how that parameter is + used in that template. Either by first analyzing how all template + parameters are used in all templates, or by recursivly searching + the called template when we find that a RTF is used as an argument. +

+ +

+ We will still need a catch-all RTF implementation that is used when + we can't determin how a variable is used, or when it is used in + multiple different ways. +

+ + +

Inline/calculate constant variables

+

+ Variables that does not have a value that depends on the source + document can be inlined into the stylesheet. The value of these + variables could also be calculated before the transformation is + started. +

+ +

+ This can be combined with the previous optimization of RTFs so that + stylesheets like +

+<xsl:variable name="v">_</xsl:variable>
+...
+<xsl:value-of select="concat(@id, $v, foo)"/>
+
+ is executed like +
+<xsl:value-of select="concat(@id, '_', foo)"/>
+
+

+ +

+ This can be done for both global and local variables. +

+ + +

Resolve cross-references

+

+ Cross-references to named templates, attribute-sets and template-modes + can be resolved before the stylesheet is executed. That way we don't + have to search for the template/attribute-set with a certain name, or + the group of templates for a certain mode. +

+ +

+ Note that for apply-imports we won't always know at compile-time which + mode to seach for templates in. +

+ + +

Resolve variables by index rather then expanded name

+

+ Rather then seaching for variables with a certain name at runtime + we could remove names for variables/parameters entierly and put all + variables in an array (possibly a separate array for global variables) + and then let variable-references in expressions just point to an index + in that array. +

+ + +

Calculate and use constant AVTs

+

+ A lot of instruction-elements uses AVTs that often are constants in + stylesheets, such as the data-type parameter to xsl:sort. For xsl:sort + we could pre-create a finished nodesorter if all AVTs in all + xsl:sort-elements are constant. +

+ +

+ Another example is xsl:element and xsl:attribute where we could use + LRE-instructions if the AVTs are constant. For xsl:number we could + create prepare the list of txFormattedCounters. +

+ + +

Reuse parameter-map if exact same parameters are used

+

+ If a template calls another template and uses the exact same + parameters we can reuse the same parameter-map. +

+<xsl:template name="hsbc_html_body">
+  <xsl:param name="appName"/>
+  <xsl:param name="title"/>
+  <body>
+    <xsl:call-template name="hsbc_form">
+      <xsl:with-param name="appName" select="$appName"/>
+      <xsl:with-param name="title" select="$title"/>
+    </xsl:call-template>
+  </body>
+</xsl:template>
+
+

+ +

+ We have to watch out for default parameter-values as well as + parameters being specified by the caller of this template but not used + by this template. I.e. if the parameter "hello" is specified when + calling the above template. +

+ +

+ All this might make this optimization useless. +

+ +

Combind consecutive LRE items into a txResultBuffer

+

+ When a template contains several consecutive "LRE instructions" such + as LRE-elements, LRE-attributes and textnodes we can replace their + instructions with a special instruction that contains a + txResultBuffer. This buffer would then be flushed when + the instruction is executed. This can of course not be done for + LRE-attributes that contains AVTs. Even PI and comment-instructions + can be included in this buffer if their contents is strictly literal. +

+ +

+ The result is that a template like +

+<xsl:template match="/">
+  <html>
+    <body bgcolor="green">
+      Here goes pagecontent
+      <xsl:apply-templates />
+    </body>
+  </html>
+</xsl:template>
+
+ Would result in the first txResultBuffer to contain the + following transactions: eStartElementTransaction, + eStartElementTransaction, eAttributeTransaction, eCharacterTransaction. +

+ +

+ There is probably a lower limit to when it's worth the effort to + replace the normal instructions with the buffer-instruction. A single + "LRE instruction" is probobably better kept as a normal instruction. +

+ + + diff --git a/mozilla/extensions/transformiix/docs/optimized-xpath.html b/mozilla/extensions/transformiix/docs/optimized-xpath.html new file mode 100644 index 00000000000..c5bbc96156c --- /dev/null +++ b/mozilla/extensions/transformiix/docs/optimized-xpath.html @@ -0,0 +1,490 @@ + + + Optimizing XPath + + + + +

Optimizing XPath

+ +

Overview

+

+ This document outlines optimizations that we can perform to execute + xpath-expressions faster. +

+ +

Stage 1, DONE

+ +

Summary

+

+ Speed up retrieval of orderInfo objects by storing them in resp. + node instead of in a hash. +

+ +

Details

+

+ We currently spend a GREAT deal of time looking through a + DOMHelper::orders hash looking for the orderInfo object for a + specific node. If we moved the ownership and retrieval of these + orderInfo objects to the Node class instead we will probably save + a lot of time. I.E. instead of calling + myDOMHelper->getDocumentOrder(node) you call + node->getDocumentOrder() which then returns the + orderInfo object. +

+ +

+ It would also be nice if we at the same time fixed some bugs wrt the + orderInfo objects and the function that sorts nodes using them. +

+ +

+ Bugs filed at this are 88964 and 94471 +

+ + + +

Stage 2, DONE

+ + +

Summary

+

+ Speed up document-order sorting by having the XPath engine always + return document-ordered nodesets. +

+ +

Details

+

+ Currently the nodesets returned from the XPath engine are totally + unordered (or rather, have undefined order) which forces the XSLT + code to sort the nodesets. This is quite expensive since it requires + us to generate orderInfo objects for every node. Considering that + many XPath classes actually returns nodesets that are already + ordered in document order (or reversed document order) this seems a + bit unnecessary. +

+ +

+ However we still need to handle the classes that don't by default + return document-ordered nodesets. A good example of this is the id() + function. For example "id('foo bar')" produces two nodes which the + id-function has no idea how they relate in terms of document order. + Another example is "foo | bar", where the UnionExpr object gets two + nodesets (ordered in document order since all XPath classes should + now return ordered nodesets) and need to merge them into a single + ordered nodeset. +

+ +

Stage 3

+ +

Summary

+

+ Speed up evaluation of XPath expressions by using specialized + classes for common optimizable expressions. +

+ +

Details

+

+ Some common expressions are possible to execute faster if we have + classes that are specialized for them. For example the expression + "@foo" can be evaluated by simply calling |context->getAttributeNode + ("foo")|, instead we now walk all attributes of the context node and + filter each node using a AttributeExpr. Below is a list of + expressions that I can think of that are optimizable, but there are + probably more. +

+ +

+ One thing that we IMHO should keep in mind is to only put effort on + optimising expressions that are actually used in realworld + stylesheets. For example "foo | foo", "foo | bar[0]" and + "foo[position()]" can all be optimised to "foo", but since noone + should be so stupid as to write such an expression we shouldn't + spend time or codesize on that. Of course we should return the + correct result according to spec for those expressions, we just + shouldn't bother with evaluating them fast. +

+ + +

+ Apart from finding expression that we can evaluate more cleverly + there is also the problem of how and where do we create these + optimised objects instead of the unoptimised, general ones we create + now. And what are these optimised classes, should they be normal + Expr classes or should they be something else? We could also add + "optional" methods to Expr which have default implementations in + Expr, for example a ::isContextSensitive() which returns MB_TRUE + unless overridden. However we probably can't answer all this until + we know which expressions we want to optimised and how we want to + optimise them. +

+ +

+ These expressions can be optimised: +

+ +

+

Class:

+ + Steps along the attribute axis which doesn't contain wildcards +

+

Example:

+ + @foo + +

What we do today:

+ + Walk through the attributes NamedNodeMap and filter each node using a + NameTest. + +

What we could do:

+ + Call getAttributeNode (or actually getAttributeNodeNS) on the + contextnode and return a nodeset containing just the returned node, or + an empty nodeset if NULL is returned. + +

+ +

+

Class:

+ + Union expressions where each expression consists of a LocationStep and + all LocationSteps have the same axis. None of the LocationSteps have any + predicates (well, this could be relaxed a bit) + +

Example:

+ + foo | bar | baz + +

What we do today:

+ + Evaluate each LocationStep separately and thus walk the same path through + the document each time. During the walking the NodeTest is applied to + filter out the correct nodes. The resulting nodesets are then merged and + thus we generate orderInfo objects for most nodes. + +

What we could do:

+ + Have just one LocationStep object which contains a NodeTest that is a + "UnionNodeTest" which contains a list of NodeTests. The UnionNodeTest + then tests each NodeTest until it finds one that returns true. If none + do then false is returned. + This results in just one walk along the axis and no need to generate any + orderInfo objects. + +

+ +

+

Class:

+ + Steps where the predicates isn't context-node-list sensitive. + +

Example:

+ + foo[@bar] + +

What we do today:

+ + Build a nodeset of all nodes that match 'foo' and then filter the + nodeset through the predicate and thus do some node shuffling. + +

What we could do:

+ + Create a "PredicatedNodeTest" that contains a NodeTest and a list of + predicates. The PredicatedNodeTest returns true if both the NodeTest + returns true and all predicats evaluate to true. Then let the + LocationStep have that PredicateNodeTest as NodeTest and no predicates. + This will save us the predicate filtering and thus some node shuffling. + (Note how this combines nicely with the previous optimisation...) + (Actually this can be done even if some predicates are context-list + sensitive, but only up until the first that isn't.) + +

+ +

+

Class:

+ + PathExprs that only contains steps that from the child:: and attribute:: + axes. + +

Example:

+ + foo/bar/baz + +

What we do today:

+ + For each step we evaluate the step once for every node in a nodeset + (for example for the second step the nodeset is the list of all "foo" + children) and then merge the resulting nodesets while making sure that + we keep the nodes in document order (and thus generate orderInfo + objects). + +

What we could do:

+ + The same thing except that we don't merge the resulting nodeset, but + rather just concatenate them. We always know that the resulting nodesets + are after each other in node order. + +

+ +

+

Class:

+ + List of predicates where some predicate are not context-list sensitive + +

Example:

+ + foo[position() > 3][@bar][.//baz][position() > size() div 2][.//@fud] + +

What we do today:

+ + Apply each predicate separately requiring us to shuffle nodes five times + in the above example. + +

What we could do:

+ + Merge all predicates that are not node context-list sensitive into the + previous predicate. The above predicate list could be merged into the + following predicate list + foo[(position() > 3) and (@bar) and (.//baz)][(position() > size() div 2) and (.//@fud)] + Which only requires two node-shuffles + +

+ +

+

Class:

+ + Predicates that are only context-list-position sensitive and not + context-list-size sensitive + +

Example:

+ + foo[position() > 5][position() mod 2] + +

What we do today:

+ + Build the entire list of nodes that matches "foo" and then apply the + predicates + +

What we could do:

+ + Apply the predicates during the initial build of the first nodeset. We + would have to keep track of how many nodes has passed each and somehow + override the code that calculates the context-list-position. + +

+ +

+

Class:

+ + Predicates that are constants + +

Example:

+ + foo[5] + +

What we do today:

+ + Perform the appropriate walk and build the entire nodeset. Then apply + the predicate. + +

What we could do:

+ + There are three types of constant results; 1) Numerical values 2) + Results with a true boolean-value 3) Results with a false boolean value. + In the case of 1) we should only step up until the n:th node (5 in above + example) and then stop. For 2) we should completely ignore the predicate + and for 3) we should return an empty nodeset without doing any walking. + In some cases we can't at parsetime decide if a constant expression will + return a numerical or not, for example for "foo[$pos]", so the decision + of 1) 2) or 3) would have to be made at evaltime. However we should be + able to decide if it's a constant or not at parsetime. + Note that while evaluating a LocationStep [//foo] can be considered + constant. + +

+ +

+

Class:

+ + PathExprs that contains '//' followed by an unpredicated child-step. + +

Example:

+ + .//bar + +

What we do today:

+ + We walk the entire subtree below the contextnode and at every node we + evaluate the 'bar'-expression which walks all the children of the + contextnode. This means that we'll walk the entire subtree twice. + +

What we could do:

+ + Change the expression into "./descendant::bar". This means that we'll + only walk the tree once. This can only be done if there are no + predicates since the context-node-list will be different for + predicates in the new expression. + Note that this combines nicely with the "Steps where the predicates + isn't context-node-list sensitive" optimization. + +

+ +

+

Class:

+ + PathExprs where the first step is '.' + +

Example:

+ + ./* + +

What we do today:

+ + Evaluate the step "." which always returns the same node and then + evaluate the rest of the PathExpr. + +

What we could do:

+ + Remove the '.'-step and simply evaluate the other steps. In the example + we could even remove the entire PathExpr-object and replace it with a + single Step-object. + +

+ +

+

Class:

+ + Steps along the attribute axis which doesn't contain wildcards and + we only care about the boolean value. + +

Example:

+ + foo[@bar], @foo or @bar + +

What we do today:

+ + Evaluate the step and create a nodeset. Then get the bool-value of + the nodeset by checking if the nodeset contain any nodes. + +

What we could do:

+ + Simply check if the current element has an attribute of the + requested name and return a bool-result. + +

+ +

+

Class:

+ + Steps where we only care about the boolean value. + +

Example:

+ + foo[processing-instruction()] + +

What we do today:

+ + Evaluate the step and create a nodeset. Then get the bool-value of + the nodeset by checking if the nodeset contain any nodes. + +

What we could do:

+ + Walk along the axis until we find a node that matches the nodetest. + If one is found we can stop the walking and return a true + bool-result immediatly, otherwise a false bool-result is returned. + It might not be worth implementing all axes unless we can reuse + code from the normal Step-code. + +

+ +

Stage 4

+ +

Summary

+

+ Refcount ExprResults to reduce the number of objects + created during evaluation. +

+ +

Details

+

+ Right now every subexpression creates a new object during evaluation. + If we refcounted objects we would be often be able to reuse the same + objects across multiple evaluations. We should also keep global + result-objects for true and false, that way expressions that return + bool-values would never have to create any objects. +

+ +

+ This does however require that the returned objects arn't modified + since they might be used elsewhere. This is not a big problem in the + current code where we pretty much only modify nodesets in a couple + of places. +

+ +

+ To be able to reuse objects across subexpressions we chould have an + ExprResult::ensureModifyable-function. This would + return the same object if the refcount is 1, and create a new object + to return otherwise. This is especially usefull for nodesets which + would be mostly used by a single object at a time. But it could be + just as usefull for other types, though then we might need a + ExprResult::ensureModifyableOfType(ExprResult::ResultType)-function + that only returned itself if it has a refcount of 1 and is of the + requsted type. +

+ +

Stage 5

+ +

Summary

+

+ Detect when we can concatenate nodesets instead of merge them in + PathExpr. +

+ +

Details

+

+ Why can we for expressions like "foo/bar/baz" concatenate the resulting + nodesets without having to check nodeorder? Because at every step two + statements are true: +

    +
  1. We iterate a nodeset where no node is an ancestor of another
  2. +
  3. The LocationStep only returns nodes that are members of the subtree + below the context-node
  4. +
+

+ +

+ For example; While evaluating the second step in "foo/bar/baz" we + iterate a nodelist containing all "foo" children of the original + contextnode, i.e. none can be an ancestor of another. And the + LocationStep "bar" only returns children of the contextnode. +

+ +

+ So, it would be nice if we can detect when this occurs as often as + possible. For example the expression "id(foo)/bar/baz" fulfils those + requirements if the nodeset returned from contains doesn't contain any + ancestors of other nodes in the nodeset, which probably often is the + case in real-world stylesheets. +

+ +

+ We should perform this check on every step to be able to take advantage + of it as often as possible. For example the in expression + "id(@boss)/ancestor::team/members" we can't use this optimisation at the + second step since the ancestor axis returns nodes that are not members + of the contextnodes subtree. However we will probably be able to use the + optimisation at the third step since if iterated nodeset contains only + one node (and thus can't contain ancestors of it's members). +

+ +