Report on slow last()
Added by Anonymous almost 16 years ago
Legacy ID: #6300975 Legacy Poster: Vladimir Nesterovsky (vnesterovsky)
Hello, I want to report a sample when last() function is too slow: <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsl:template match="/" name="main"> <xsl:variable name="elements" as="element()"> <xsl:for-each select="1 to 2000"> <element id="{.}"/> </xsl:for-each> </xsl:variable> <xsl:variable name="ids" as="xs:string" select=" for $i in 1 to 2000 return ( xs:string($i) )"/> <xsl:variable name="items" as="element()*" select="$elements[@id = $ids]"/> <xsl:for-each select="$items"> <!-- <xsl:sequence select="last()"/> --> <xsl:message> <xsl:text>Id: </xsl:text> <xsl:value-of select="@id"/> <xsl:text>, </xsl:text> <xsl:value-of select="position()"/> <xsl:text> out of </xsl:text> <xsl:value-of select="last()"/> <!-- <xsl:value-of select="count($items)"/> --> </xsl:message> </xsl:for-each> </xsl:template> </xsl:stylesheet> Thanks.
Replies (3)
Please register to reply
RE: Report on slow last() - Added by Anonymous almost 16 years ago
Legacy ID: #6303019 Legacy Poster: Michael Kay (mhkay)
Thanks for reporting this. This must have been in the product for ever! As a workaround, pre-compute last() outside the xsl:message, for example <xsl:for-each select="$items"> <xsl:if test="last() lt 0">can't happen</xsl:if> <xsl:message> ... The problem will occur whenever (a) last is executed on a sequence whose size isn't known in advance, and (b) it's executed in an "inner context" such as the one set up by xsl:message. What's happening here is that although xsl:message doesn't change the context at the language level, it creates a new output destination, and that causes a new context to be created internally. The value of last() is cached in the context object, but it's being stored in the inner context and not passed back to the outer context when xsl:message completes, so the next time around the iteration, there is no cached value, and last() is recomputed from scratch, which means reading all the way to the end of the sequence. This would also happen if you replaced xsl:message by xsl:variable or xsl:result-document. I've devised a fix, which involves holding the cached value of last() in a mutable object shared between all context objects that share the same current iterator. However, I'm going to leave this till the next development release to ensure that it's fully stable - I don't normally issue patches for performance problems unless they're critical.
RE: Report on slow last() - Added by Anonymous almost 16 years ago
Legacy ID: #6315863 Legacy Poster: Vladimir Nesterovsky (vnesterovsky)
What's interesting is that the <xsl:value-of select="count($items)"/> is evaluated and cached in the injected variable, while last() is not.
RE: Report on slow last() - Added by Anonymous almost 16 years ago
Legacy ID: #6316035 Legacy Poster: Michael Kay (mhkay)
Yes, count($items) is recognized as having no dependency on the context, so it can be evaluated outside the for-each loop. There's currently no attempt to do any static analysis on last() - it relies entirely on dynamic optimizations (which usually work perfectly well). I have a long-standing TODO to explore what can be done with it statically. There is one difference: using a variable and count() will cause the value of the sequence to be materialized in memory, whereas using last() causes the controlling expression to be evaluated twice, without storing the results (the only thing cached is the count). Sometimes one strategy is better, sometimes the other.
Please register to reply