我是一名支持工程师,我们公司的产品允许XSLT转换定制输出。
我为此目的进行了xsl转换。它适用于典型大小的源文件(几个100k),但偶尔会有一个非常大的(10M)源文件。在这种情况下,即使我让它磨了几天也不会产生输出。
SW工程团队测试了它并发现对于转换和大型源文件确实非常慢(>天),如果我们的产品被编译为使用.Net 1.1中的转换引擎,但是如果它们编译它与.Net 2.0一样,速度很快(约1-2分钟)。
长期解决方案显然是等待下一个版本。
从短期来看,我想知道以下几点: 1)XSLT是否足够灵活,是否有更高效,更低效的方法来实现相同的结果?例如,有可能我构建xsl的方式,转换引擎必须多次从源文件的开头迭代,越长越长,因为下一个结果片从一开始就越走越远? (Schlemiel the Painter),或 2)它是否更依赖于变换引擎如何解释xsl?
如果是2,我不想浪费大量时间来改进xsl(我不是一个很大的xsl天才,我很难实现我做的很少......)
谢谢!
答案 0 :(得分:5)
我不熟悉.NET实现,但是通常可以做一些事情来加速大型文档的处理:
答案 1 :(得分:4)
通常,如果您发现处理时间与输入大小的非线性增加,您应该怀疑代码多于框架。但是,当使用.NET 2.0编译该工具时问题就消失了,所有的赌注都没有了。
使用XSLT,如果您使用直接模板匹配进行所有解析,则很难创建非线性性能曲线:
<xsl:template match="foo">
<!--OUTPUT-->
<xsl:apply-templates / >
<!--OUTPUT-->
</xsl:template>
<xsl:template match="bar">
<!--OUTPUT-->
<xsl:apply-templates / >
<!--OUTPUT-->
</xsl:template>
请注意您可能使用<xsl:for-each>
进行解析的任何地方;模板匹配几乎总是获得相同结果的更好方法。
解决此性能问题的一种方法是一次重新创建一个模板匹配的XSLT,在添加每个匹配项后测试处理时间。你可以从这场比赛开始:
<xsl:template match="*">
<xsl:copy> <!--Copy node -->
<xsl:copy-of select="@*"/> <!--Copy node attributes -->
<xsl:apply-templates /> <!--Process children -->
</xsl:copy>
</xsl:template>
这将匹配并将每个节点(一次一个)复制到新文档。这不应该表现出处理时间与输入大小的非线性增加(如果确实如此,则问题不在于您的XSLT代码)。
在重新创建XSLT时,如果添加突然杀死性能的模板匹配,请注释掉模板中的每个块。然后,一次取消注释一个块,测试每次迭代的处理时间,直到找到导致问题的块为止。
答案 2 :(得分:3)
要检测何时开始新的部分,我这样做了:
<xsl:if test="@TheFirstCol>preceding-sibling::*[1]/@TheFirstCol"
这会导致很多或重复吗?
当然。您选择的算法是O(N 2 ),并且在有足够数量的兄弟姐妹的情况下会非常慢,无论实现语言如何。
这是一个使用密钥的有效算法:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kC1Value" match="@c1" use="."/>
<xsl:template match="/">
<xsl:for-each select="*/x[generate-id(@c1) = generate-id(key('kC1Value',@c1)[1])]">
<xsl:value-of select="concat('
',@c1)"/>
<xsl:for-each select="key('kC1Value',@c1)">
<xsl:value-of select="'
'"/>
<xsl:for-each select="../@*[not(name()='c1')]">
<xsl:value-of select="concat(' ', .)"/>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
不幸的是,XslTransform(.Net 1.1)的generate-id()
函数的执行效率非常低。
使用XslTransform可能会更快:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kC1Value" match="@c1" use="."/>
<xsl:template match="/">
<xsl:for-each select="*/x[count(@c1 | key('kC1Value',@c1)[1]) = 1]">
<xsl:value-of select="concat('
',@c1)"/>
<xsl:for-each select="key('kC1Value',@c1)">
<xsl:value-of select="'
'"/>
<xsl:for-each select="../@*[not(name()='c1')]">
<xsl:value-of select="concat(' ', .)"/>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
应用于以下小型XML文档时:
<t>
<x c1="1" c2="0" c3="0" c4="0" c5="0"/>
<x c1="1" c2="0" c3="1" c4="0" c5="0"/>
<x c1="1" c2="2" c3="0" c4="0" c5="0"/>
<x c1="1" c2="1" c3="1" c4="0" c5="0"/>
<x c1="2" c2="0" c3="0" c4="0" c5="0"/>
<x c1="2" c2="0" c3="1" c4="0" c5="0"/>
<x c1="2" c2="2" c3="0" c4="0" c5="0"/>
<x c1="2" c2="1" c3="1" c4="0" c5="0"/>
<x c1="3" c2="0" c3="0" c4="0" c5="0"/>
<x c1="3" c2="0" c3="1" c4="0" c5="0"/>
<x c1="3" c2="2" c3="0" c4="0" c5="0"/>
<x c1="3" c2="1" c3="1" c4="0" c5="0"/>
<x c1="3" c2="0" c3="0" c4="0" c5="0"/>
<x c1="3" c2="0" c3="1" c4="0" c5="0"/>
<x c1="3" c2="2" c3="0" c4="0" c5="0"/>
<x c1="3" c2="1" c3="1" c4="0" c5="0"/>
<x c1="4" c2="0" c3="0" c4="0" c5="0"/>
<x c1="4" c2="0" c3="1" c4="0" c5="0"/>
<x c1="4" c2="2" c3="0" c4="0" c5="0"/>
<x c1="4" c2="1" c3="1" c4="0" c5="0"/>
<x c1="5" c2="0" c3="0" c4="0" c5="0"/>
<x c1="5" c2="0" c3="1" c4="0" c5="0"/>
<x c1="5" c2="2" c3="0" c4="0" c5="0"/>
<x c1="5" c2="1" c3="1" c4="0" c5="0"/>
<x c1="5" c2="0" c3="0" c4="0" c5="0"/>
<x c1="5" c2="0" c3="1" c4="0" c5="0"/>
<x c1="6" c2="2" c3="0" c4="0" c5="0"/>
<x c1="6" c2="1" c3="1" c4="0" c5="0"/>
<x c1="6" c2="0" c3="0" c4="0" c5="0"/>
<x c1="6" c2="0" c3="1" c4="0" c5="0"/>
<x c1="6" c2="2" c3="0" c4="0" c5="0"/>
<x c1="6" c2="1" c3="1" c4="0" c5="0"/>
<x c1="7" c2="0" c3="0" c4="0" c5="0"/>
<x c1="7" c2="0" c3="1" c4="0" c5="0"/>
<x c1="7" c2="2" c3="0" c4="0" c5="0"/>
<x c1="7" c2="1" c3="1" c4="0" c5="0"/>
<x c1="8" c2="0" c3="0" c4="0" c5="0"/>
<x c1="8" c2="0" c3="1" c4="0" c5="0"/>
<x c1="8" c2="2" c3="0" c4="0" c5="0"/>
<x c1="8" c2="1" c3="1" c4="0" c5="0"/>
</t>
这两种解决方案产生了想要的结果:
1
0 0 0 0
0 1 0 0
2 0 0 0
1 1 0 0
2
0 0 0 0
0 1 0 0
2 0 0 0
1 1 0 0
3
0 0 0 0
0 1 0 0
2 0 0 0
1 1 0 0
0 0 0 0
0 1 0 0
2 0 0 0
1 1 0 0
4
0 0 0 0
0 1 0 0
2 0 0 0
1 1 0 0
5
0 0 0 0
0 1 0 0
2 0 0 0
1 1 0 0
0 0 0 0
0 1 0 0
6
2 0 0 0
1 1 0 0
0 0 0 0
0 1 0 0
2 0 0 0
1 1 0 0
7
0 0 0 0
0 1 0 0
2 0 0 0
1 1 0 0
8
0 0 0 0
0 1 0 0
2 0 0 0
1 1 0 0
从上面的小XML文件中,我通过复制每个元素6250次(使用另一个XSLT转换:)生成了一个10MB的XML文件。)。
使用10MB xml文件和XslCompiledTransform(.Net 2.0 +),这两种解决方案具有以下转换时间:
解决方案1:3.3秒 解决方案2:2.8秒。
使用XslTransform(.Net 1.1)解决方案2运行1622秒。那是大约27分钟。
答案 3 :(得分:2)
世界检查的一件事是你的XSLT是否经常查找XML文档的其他部分,即你在一个上下文节点中并在文档的另一部分甚至另一个文档中查找值。如果你这样做可能会很难达到性能,你应该考虑使用xsl:key和关键功能。它告诉处理器对相关数据实现快速查找索引。
我曾经构建过一个需要8个小时才能运行的XSLT(有很多交叉引用)并且切换到使用密钥会给它带来巨大的速度提升。
答案 4 :(得分:1)