在XSLT程序中的某个时刻,我有以下内容:
<xsl:for-each select="tags/tag">
<xsl:apply-templates select="//shows/show[film=//films/film[tag=current()/@id]/@id]|//shows/show[group=//groups/group[film=//films/film[tag=current()/@id]/@id]/@id]">
<xsl:sort select="date" data-type="text" order="ascending"/>
<xsl:sort select="time" data-type="text" order="ascending"/>
</xsl:apply-templates>
</xsl:for-each>
似乎XPath表达式//shows/show[film=//films/film[tag=current()/@id]/@id]|//shows/show[group=//groups/group[film=//films/film[tag=current()/@id]/@id]/@id]
相当复杂,大大减慢了程序的执行速度(与添加引用代码之前的执行时间相比 - 处理相同的数据,当然)。
你认为这是正常的,因为表达的性质相对复杂,你是否看到我如何改进它以使其表现更好?
注意:在XPath表达式中,film
和//films/film
,group
和//groups/group
引用了不同的元素。
请参阅下面的XML输入的精简样本。
<program>
<tags>
<tag id="1">Tag1</tag>
<tag id="2">Tag2</tag>
<tag id="3">Tag3</tag>
</tags>
<films>
<film id="1">
Film1
<tag>2</tag><!-- References: /program/tags/tag/@id=2 -->
</film>
<film id="2">
Film2
<tag>1</tag><!-- References: /program/tags/tag/@id=1 -->
</film>
<film id="3">
Film3
<tag>3</tag><!-- References: /program/tags/tag/@id=3 -->
</film>
<film id="4">
Film4
<tag>3</tag><!-- References: /program/tags/tag/@id=3 -->
</film>
</film>
<groups>
<group id="1">
<film>3</film><!-- References: /program/films/film/@id=3 -->
<film>4</film><!-- References: /program/films/film/@id=4 -->
</group>
</groups>
<shows>
<show id="1"><!-- Show with film (=simple) -->
<film>1</film><!-- References: /program/films/film/@id=1 -->
<date>2011-12-12</date>
<time>12:00</time>
</show>
<show id="2"><!-- Show with group (=combined) -->
<group>1</group><!-- References: /program/groups/group/@id=1 -->
<date>2011-12-12</date>
<time>14:00</time>
</show>
</shows>
</program>
说明:
答案 0 :(得分:6)
Double slashes in XPath是性能和CPU占用(因为必须评估文档中的每个节点)。如果您可以用绝对或相对路径替换它,您应该有明显的改进。如果您可以发布输入架构和所需的输出,我们可能更具体一点吗?
e.g。有绝对路径
//shows/show[film=//films/film[tag=current()/@id]/@id]
变为
/myroot/somepath/shows/show[film=/myroot/somepath/films/film[tag=current()/@id]/@id]
或者节目和电影是否相对于当前节点
./relativexpath/shows/show[film=./relativexpath/somepath/films/film[tag=current()/@id]/@id]
答案 1 :(得分:3)
nonnb的答案非常可能指向问题,但不是真正的有效解决方案(“更便宜”的轴更好,但仅靠这一点并不能提高速度,例如索引数据时)。
请注意,最大的问题是XPath表达式谓词为每个评估执行树的另一次完整遍历。你应该使用键来做这样的事情;这将(在大多数甚至所有XSLT实现中)使索引查找成为可能,从而减少运行时间。
通过id:
定义电影,小组和节目的按键<xsl:key name="filmByTag" match="film" use="tag" />
<xsl:key name="groupsByFilm" match="group" use="tag" />
<xsl:key name="showsByFilm" match="show" use="film" />
<xsl:key name="showsByGroup" match="show" use="group" />
然后像这样使用它(未经测试,但你应该明白这一点):
<xsl:variable name="films" select="key('filmByTag', @id)/@id" />
<xsl:apply-templates select="key('showsByFilm', $films)/@id|key('showsByGroups', key('groupsByFilm', $films)/@id)/@id">
答案 2 :(得分:2)
您的XPath表达式似乎正在进行三向连接,因此除非经过优化,否则性能可能是源文档大小的O(n ^ 3)。优化涉及通过索引查找替换文档的序列搜索。有两种方法可以实现这一点:您可以通过使用key()函数上的调用替换过滤器表达式来手动优化它(如Dimitre所示),或者您可以使用优化的XSLT处理器,例如Saxon-EE,自动执行相同的优化。
答案 3 :(得分:1)
使用xsl:key
定义密钥,然后使用key
函数进行交叉引用,而不是当前的比较。向我们展示XML的示例,以便我们可以理解其结构,然后我们可以帮助您使用具体的代码。
答案 4 :(得分:1)
以下是两个应该表现出更好性能的完整解决方案:
请注意:仅在足够大的输入样本上才会注册更好的性能。在小输入样本上,优化它是不值得的。
<强>予。不使用//
(但不使用密钥)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vFilms" select="/*/films/film"/>
<xsl:variable name="vShows" select="/*/shows/show"/>
<xsl:variable name="vGroups" select="/*/groups/group"/>
<xsl:variable name="vTags" select="/*/tags/tag"/>
<xsl:template match="/*">
<xsl:for-each select="$vTags">
<xsl:apply-templates select=
"$vShows
[film
=
$vFilms
[tag=current()/@id]
/@id
or
group
=
$vGroups
[film
=
$vFilms
[tag=current()/@id]
/@id
]
/@id
]
">
<xsl:sort select="date" data-type="text" order="ascending"/>
<xsl:sort select="time" data-type="text" order="ascending"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="show">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
<强> II。使用密钥
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kShowByFilmId" match="show"
use="film"/>
<xsl:key name="kShowByGroupId" match="show"
use="group"/>
<xsl:key name="kGroupByFilmId" match="group"
use="film"/>
<xsl:key name="kFilmByTag" match="film"
use="tag"/>
<xsl:variable name="vTags" select="/*/tags/tag"/>
<xsl:template match="/*">
<xsl:for-each select="$vTags">
<xsl:apply-templates select=
"key('kShowByFilmId',
key('kFilmByTag', current()/@id)/@id
)
|
key('kShowByGroupId',
key('kGroupByFilmId',
key('kFilmByTag', current()/@id)/@id
)
/@id
)
">
<xsl:sort select="date" data-type="text" order="ascending"/>
<xsl:sort select="time" data-type="text" order="ascending"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="show">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>