27 January 2005

XSLT performance issue using "contains"

A weird performance error has occurred in one of our solutions.

The following line of XSLT takes up to 1 second to execute:
<xsl:if test="$start/item[contains($templates,concat('!',@template,'!'))] != ''">

  • $templates contains a string like "!rootfolder!folder!document!externalmenulink!"
  • @template contains a string like "rootfolder"
  • The number of items under $start is less than 10
This should not be a hard task and it is not in most cases. In a for-each loop with changing @template and $start values only some calls are slow. Others execute in less than a millisecond. And the fast ones in some cases have the exact same @template value as the slow ones - and even more items under $start to check.

One could imagine that this behavior was related to other elements stressing the server. But as the same four calls fails every time the error must be related to them in someway. They must have some common characteristics. I just have not been able to identify those!

But I did find out that modifying the test attribute to
<xsl:if test="count($start/item[contains($templates,concat('!',@template,'!'))]) != '0'">
...solved the problem! Every call now executes in less than a millisecond.

As the functionality is exactly the same I strongly recommend using the later even though the problem does not occur in general when using the other.

1 comment:

commodore73 said...

Are you sure the problem is not with the comparison against an empty string? I believe an empty nodeset in XSL returns false so it may still work and be faster as:

xsl:if test="$start/item[contains($templates,concat('!',@template,'!'))]"

Of course I am not sure this would work at all, but the other way I expect it has to parse/remove markup from the entire substructure which wouldn't seem very efficient. I would expect this method to be faster than count().