[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

RE: Slow XSLT

Subject: RE: Slow XSLT
From: Cleyton Jordan <cleytonjordan@xxxxxxxxxxx>
Date: Sun, 2 Mar 2008 13:37:25 +0000 (GMT)
RE:  Slow XSLT
Hi Michael,

Michael wrote:

> Its easy to see why your code is slow. But I'm
> afraid I can't suggest any
> way to speed it up, because I simply can't work out
> what it is trying to do.

Please find below my previous posts and replies I got.
I hope it becomes clearer what I am trying to do. If
not, please let me know so that I can try and come up
with a better explanation.

Cheers

C
++++++++++
I need to build a nested heading Rows/Columns (<TR>
and <TD>). The output should look like this for the
2003 heading:

                        2003
Quarter 1        Quarter 2        Quarter 3
    Quarter 4

 <ColGrp heading="Quarter">
   <ColGrp heading="2003">
     <Col heading="Quarter 1" />
     <Col heading="Quarter 2" />
     <Col heading="Quarter 3" />
     <Col heading="Quarter 4" />
   </ColGrp>
  </ColGrp>


I use the xpath below to get me to the inner most
ColGrp (parent of Col elements).

Normally $depth = 1. This means I do not want to
output the outer most ColGrp - <ColGrp
heading="Quarter">

//ColGrp[count(ancestor::ColGrp)=$depth]

//ColGrp[count(ancestor::ColGrp)=1]

So, I go to the ancestor:ColGrp whose depth = 1
This will produce the first <TD>s (

2003      2004

Then all the Col elements will produce the second
row with all the <TD>s

Quarter 1   Quarter 2      Quarter 3       Quarter
4

And lastly, for each Col element I have to go back
to the Measure elements to produce the final Row which
will be the same for all the Col elements

P.S. The last row and <td>s come from the Measure
elements and I use the position() function to output
them like that:
 
Total Pages Cost Total Pages Cost Total Pages Cost
Total Pages Cost

Therefore, this is the output I get with my XSLT
but it is too slow for big xml docs.

                          2003
Quarter 1     Quarter 2    Quarter 3        Quarter
4
Total Pages Cost Total Pages Cost Total Pages Cost


<Measures>
  <Measure idx="1" heading="Total Pages" />
  <Measure idx="2" heading="Cost" />
</Measures>
<Columns>
 <ColGrp heading="Quarter">
   <ColGrp heading="2003">
     <Col heading="Quarter 1" />
     <Col heading="Quarter 2" />
     <Col heading="Quarter 3" />
     <Col heading="Quarter 4" />
   </ColGrp>
  </ColGrp>
</Columns>


++++++++++++++++++

Hi Manfred,

Thanks for your reply.

I think I can now understand what David poited out
i.e. that when using // can be very costly. 

However, I thought that by using // followed by a
predicate ( //ColGrp[count(ancestor::ColGrp)=$depth]
), I would go straight to only those inner most ColGrp
elements parent of the Col elements.

There is one problem with your approach though. You
are assuming that there will always be only two ColGrp
like this:

<xsl:for-each
select="/Reports/Report/Columns/ColGrp/ColGrp[count(ancestor::ColGrp)=$depth]">

<ColGrp heading="Quarter">
   <ColGrp heading="2003">
     <Col heading="Quarter 1" />
     <Col heading="Quarter 2" />
     <Col heading="Quarter 3" />
     <Col heading="Quarter 4" />
   </ColGrp>
</ColGrp>

But That is not the case. The xml can have as many
nested ColGrp as necessary. For instance:

<ColGrp heading="Quarter">
   <ColGrp heading="2003">
     <ColGrp heading="Name">
       <Col heading="Quarter 1" />
       <Col heading="Quarter 2" />
       <Col heading="Quarter 3" />
       <Col heading="Quarter 4" />
    </ColGrp>
   </ColGrp>
</ColGrp>

or

<ColGrp heading="Quarter">
   <ColGrp heading="2003">
     <ColGrp heading="Name">
       <ColGrp heading="Charity Report IO">
          <Col heading="Quarter 1" />
          <Col heading="Quarter 2" />
          <Col heading="Quarter 3" />
          <Col heading="Quarter 4" />
       </ColGrp>
    </ColGrp>
   </ColGrp>
</ColGrp>

As you can see, there can be many ColGroup levels
nested inside each other. Therefore, we need an
approach that is dynamic i.e. no matter how many
nested ColGrp there are, it will produce the right
output.

>As David has pointed out the // operator is very
>costly. So I have
>replaced them by explicit xpath expressions and
>would be interested
>about the performance now. However I could not
>resist to replace
>   count(.//Col)*$msrs
>by
>   count(Col)*$msrs
>Most of the rest I have l left unchanged. I also
>noticed the
>depreciated <nobr>, which should be replaced by css
>white-space:
>nowrap.

>Manfred

><?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="axisHeads" select="'false'" />
<!--
java -jar D:\Programme\Saxon6\saxon.jar slow.xml
slow.xsl
-->
<xsl:output indent="yes"/>

 <xsl:variable name="msrs">
   <xsl:choose>
     <xsl:when
test="/Reports/Report/Measures/Measure">
       <xsl:value-of
select="count(/Reports/Report/Measures/Measure)"/>
     </xsl:when>
     <xsl:otherwise>
       <xsl:value-of select="1"/>
     </xsl:otherwise>
   </xsl:choose>
 </xsl:variable>

 <xsl:template match="Reports">
 <html>
 <body>
   <xsl:apply-templates/>
 </body>
 </html>
 </xsl:template>

 <xsl:template match="Report" >
     <!-- Top -->
     <div id="g1" style="position: absolute; top: 0px;
left: 0px;
width: 400px; height: 12px">
       <table class="grdTop" border="0"
cellspacing="1"
 cellpadding="0">
         <tbody>
           <xsl:apply-templates select="Columns" />
         </tbody>
       </table>
     </div>
  </xsl:template>

 <xsl:template match="Columns">
   <xsl:apply-templates select="ColGrp[1]"
mode="Header">
     <!-- 0 for top level heading, 1 to cut it out -->
     <xsl:with-param name="depth">
       <xsl:choose>
         <xsl:when test="$axisHeads='true'">
           <xsl:value-of select="0"/>
         </xsl:when>
         <xsl:otherwise>
           <xsl:value-of select="1"/>
         </xsl:otherwise>
       </xsl:choose>
     </xsl:with-param>
   </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="ColGrp" mode="Header">
   <xsl:param name="depth" />
   <tr>
     <!-- the very first row needs a padding cell -->
     <xsl:for-each
select="/Reports/Report/Columns/ColGrp/ColGrp[count(ancestor::ColGrp)=$depth]">
         <td colspan="{count(Col)*$msrs}"
align="center"
 style="overflow:none">
         <nobr>
           <div>
             <xsl:value-of select="@heading"/>
           </div>
         </nobr>
       </td>
     </xsl:for-each>
   </tr>
   <xsl:choose>
     <xsl:when test="ColGrp">
       <xsl:apply-templates select="ColGrp[1]"
mode="Header">
         <xsl:with-param name="depth"
select="$depth+1" />
       </xsl:apply-templates>
     </xsl:when>
     <xsl:otherwise>
       <xsl:apply-templates select="Col[1]"
mode="colHead" />
     </xsl:otherwise>
   </xsl:choose>
 </xsl:template>

 <xsl:template match="Col" mode="colHead">
   <tr>
     <xsl:for-each
select="ancestor::Columns/ColGrp/ColGrp/Col">
       <td colspan="{$msrs}" valign="top"
align="center"
 style="overflow:none">
         <nobr>
           <!-- 2003/2004 -->
           <div>
             <xsl:value-of select="@heading"/>
           </div>
         </nobr>
       </td>
     </xsl:for-each>
   </tr>
   <tr valign="bottom">
     <xsl:for-each
select="/Reports/Report/Columns/ColGrp/ColGrp/Col">
       <xsl:apply-templates
select="/Reports/Report/Measures">
         <xsl:with-param name="pos"
select="position()" />
       </xsl:apply-templates>
     </xsl:for-each>
   </tr>
 </xsl:template>

 <xsl:template match="Measures">
   <xsl:param name="pos" />
   <xsl:for-each select="Measure">
     <xsl:variable name="mPos">
       <xsl:value-of select="position()" />
     </xsl:variable>
     <td align="center">
       <nobr>
         <div class="action" style="width:90px;
overflow:none"
onclick="sortFullGrid({$mPos}, {$pos}, '{@class}')">
           <xsl:value-of select="@heading"/>
         </div>
       </nobr>
     </td>
   </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

+++++++++++++++++++++





 

--- Michael Kay <mike@xxxxxxxxxxxx> wrote:

> > 
> > I have written the XSLT below and it works fine.
> > However, when the XML has lots of RowGrp elements
> then it 
> > becomes quite slow to transform.
> 
> I see no RowGrp elements, I assume you mean
> ColGrp...
> 
> Its easy to see why your code is slow. But I'm
> afraid I can't suggest any
> way to speed it up, because I simply can't work out
> what it is trying to do.
> You haven't given any clues, and reverse engineering
> the requirements from
> some rather strangely-written code is not easy.
> 
> Michael Kay
> http://www.saxonica.com/
> 
> 
> > 
> > The difficulty that I am having is to find a way
> to speed it 
> > up. I think there could be two problems with my
> XSLT:
> > 
> > 1 - Recursion in the ColGrp Template mode="header"
>  
> > 
> > <xsl:for-each
> > select="//ColGrp[count(ancestor::ColGrp)=$depth]">
> > 
> > 2 - Also, when I am in the Col Template I have to
> go back to 
> > the measures element for each Col
> > 
> > <xsl:for-each select="//Col">
> >   <xsl:apply-templates select="//Measures">
> >     <xsl:with-param name="pos" select="position()"
> />
> >   </xsl:apply-templates>
> > </xsl:for-each>
> > 
> > I would appreciate if someone could sched some
> light.
> > I have read Jeni's XSLT pages (key and grouping)
> but have not 
> > found any posts or example similar to mine.
> > 
> > Cheers
> > 
> > C
> > ===========================
> > XML
> > <Reports>
> > <Report xmlns="">
> >   <Measures>
> >     <Measure idx="1" heading="Total Pages" />
> >     <Measure idx="2" heading="Cost" />
> >   </Measures>
> >   <Columns>
> >     <ColGrp heading="Quarter">
> >       <ColGrp heading="2003">
> >         <Col heading="Quarter 1" />
> >         <Col heading="Quarter 2" />
> >         <Col heading="Quarter 3" />
> >         <Col heading="Quarter 4" />
> >       </ColGrp>
> >       <ColGrp heading="2004">
> >         <Col heading="Quarter 1" />
> >         <Col heading="Quarter 2" />
> >         <Col heading="Quarter 3" />
> >         <Col heading="Quarter 4" />
> >       </ColGrp>
> >     </ColGrp>
> >     <!--===TOTALS====-->
> >     <ColGrp>
> >       <ColGrp>
> >         <Col heading="Total" />
> >       </ColGrp>
> >     </ColGrp>
> >   </Columns>
> > </Report>
> > </Reports>
> > 
> > ==============================
> > 
> > XSLT
> > 
> > <xsl:param name="axisHeads" select="'false'" />
> > 
> >   <xsl:variable name="msrs">
> >     <xsl:choose>
> >       <xsl:when test="//Measure">
> >         <xsl:value-of select="count(//Measure)"/>
> >       </xsl:when>
> >       <xsl:otherwise>
> >         <xsl:value-of select="1"/>
> >       </xsl:otherwise>
> >     </xsl:choose>
> >   </xsl:variable>
> > 
> >  <xsl:template match="Reports">
> >     <xsl:apply-templates
> > select="Report"></xsl:apply-templates>
> >   </xsl:template>
> >   
> >   <xsl:template match="Report" >
> >       <!-- Top -->
> >       <div id="g1" style="position: absolute; top:
> > 0px; left: 0px; width: 400px; height: 12px">
> >         <table class="grdTop" border="0"
> > cellspacing="1" cellpadding="0">
> >           <tbody>
> >             <xsl:apply-templates select="Columns"
> />
> >           </tbody>
> >         </table>
> >       </div>
> >    </xsl:template>
> > 
> >   <xsl:template match="Columns">
> >     <xsl:apply-templates select="ColGrp[1]"
> > mode="Header">
> >       <!-- 0 for top level heading, 1 to cut it
> out
> > -->
> >       <xsl:with-param name="depth">
> >         <xsl:choose>
> >           <xsl:when test="$axisHeads='true'">
> >             <xsl:value-of select="0"/>
> >           </xsl:when>
> >           <xsl:otherwise>
> >             <xsl:value-of select="1"/>
> >           </xsl:otherwise>
> >         </xsl:choose>
> >       </xsl:with-param>
> >     </xsl:apply-templates>
> >   </xsl:template>
> >  
> >   <xsl:template match="ColGrp" mode="Header">
> >     <xsl:param name="depth" />
> >     <tr>
> >       <!-- the very first row needs a padding cell
> -->
> >       <xsl:for-each
> > select="//ColGrp[count(ancestor::ColGrp)=$depth]">
> >           <td colspan="{count(.//Col)*$msrs}"
> > align="center" style="overflow:none">
> >           <nobr>
> >             <div>
> >               <xsl:value-of select="@heading"/>
> >             </div>
> >           </nobr>
> >         </td>
> >       </xsl:for-each>
> >     </tr>
> >     <xsl:choose>
> >       <xsl:when test="ColGrp"> 
> >         <xsl:apply-templates select="ColGrp[1]"
> > mode="Header">
> >           <xsl:with-param name="depth"
> > select="$depth+1" />
> >         </xsl:apply-templates>
> >       </xsl:when>
> >       <xsl:otherwise>
> >         <xsl:apply-templates select="Col[1]"
> > mode="colHead" />
> >       </xsl:otherwise>
> >     </xsl:choose>
> >   </xsl:template>
> > 
> >   <xsl:template match="Col" mode="colHead">
> >     <tr>
> >       <xsl:for-each
> select="ancestor::Columns//Col">
> >         <td colspan="{$msrs}" valign="top"
> > align="center" style="overflow:none">
> >           <nobr>
> >             <!-- 2003/2004 -->
> >             <div>
> >               <xsl:value-of select="@heading"/>
> >             </div>
> >           </nobr>
> >         </td>
> >       </xsl:for-each>
> >     </tr>
> >     <tr valign="bottom">
> >       <xsl:for-each select="//Col">
> >         <xsl:apply-templates select="//Measures">
> >           <xsl:with-param name="pos"
> > select="position()" />
> >         </xsl:apply-templates>
> >       </xsl:for-each>
> >     </tr>
> >   </xsl:template>
> > 
> >   <xsl:template match="//Measures">
> >     <xsl:param name="pos" />
> >     <xsl:for-each select="Measure">
> 
=== message truncated ===



      ___________________________________________________________ 
Rise to the challenge for Sport Relief with Yahoo! For Good  

http://uk.promotions.yahoo.com/forgood/

Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.