XSLT转换效率

Kno*_*rre 9 .net xslt

我是一名支持工程师,我们公司的产品允许XSLT转换定制输出.

为此目的我做了一个xsl转换.它适用于典型大小的源文件(几个100k),但偶尔会有一个非常大的(10M)源文件.在这种情况下,即使我让它磨几天也不会产生输出.

SW工程团队对其进行了测试并发现,对于转换和大型源文件确实非常慢(>天),如果我们的产品被编译为使用.Net 1.1中的转换引擎,但是如果他们用.Net编译它2.0,速度快(约1-2分钟).

显而易见的长期解决方案是,等待下一个版本.

从短期来看,我想知道以下几点:1)XSLT是否足够灵活,以便有更高效,效率更低的方法来实现相同的结果?例如,有可能我构造xsl的方式,变换引擎必须多次从源文件的开头迭代,越长越长,因为下一个结果片从开始越走越远?(Schlemiel the Painter),或者2)它是否更依赖于变换引擎如何解释xsl?

如果是2,我不想浪费大量时间来改进xsl(我不是一个很大的xsl天才,我很难实现我所做的一点......).

谢谢!

Mar*_*rco 5

我不熟悉.NET实现,但是通常可以做一些事情来加速大型文档的处理:

  • 除非绝对必要,否则请避免在Xpath表达式中使用"//".
  • 如果您只需要与Xpath表达式匹配的第一个或唯一元素,请使用"[1]"限定符,例如"// iframe [1]".许多处理器为此实现了优化.
  • 只要有可能,在处理大量XML输入时,看看您是否可以围绕基于流的解析器(如SAX)而不是基于DOM的解析器设计解决方案.


Dim*_*hev 3

为了检测何时开始新部分,我这样做了:

<xsl:if test="@TheFirstCol>preceding-sibling::*[1]/@TheFirstCol"
Run Code Online (Sandbox Code Playgroud)

这会导致很多或重复吗?

确实。您选择的算法是 O(N 2 ),并且无论使用哪种实现语言,如果有足够数量的同级,该算法都会非常慢。

这是使用密钥的有效算法:

解决方案1:

<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('&#xA;',@c1)"/>

       <xsl:for-each select="key('kC1Value',@c1)">
         <xsl:value-of select="'&#xA;'"/>
         <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>
Run Code Online (Sandbox Code Playgroud)

不幸的是,XslTransform (.Net 1.1) 的函数实现效率非常低generate-id()

使用 XslTransform 可能会更快:

解决方案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[count(@c1 | key('kC1Value',@c1)[1]) = 1]">

       <xsl:value-of select="concat('&#xA;',@c1)"/>

       <xsl:for-each select="key('kC1Value',@c1)">
         <xsl:value-of select="'&#xA;'"/>
         <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>
Run Code Online (Sandbox Code Playgroud)

当应用于以下小型 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>
Run Code Online (Sandbox Code Playgroud)

两种解决方案都产生了想要的结果:

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
Run Code Online (Sandbox Code Playgroud)

从上面的小 XML 文件中,我通过复制每个元素 6250 次(使用另一个 XSLT 转换:))生成了一个 10MB XML 文件。

使用 10MB xml 文件和 XslCompiledTransform (.Net 2.0 + ),两种解决方案的转换时间如下:

解1:3.3秒。
解2:2.8秒。

使用 XslTransform (.Net 1.1),Solution2 运行了 1622 秒;大约是27分钟。