XSLT 运行速度太慢

nik*_*nen 3 xml xslt performance

我有大约 100 个 XML 文件,我想将它们转换为另一个具有更好结构的文件。本示例将其转换为 CSV,但我还有一个变体,可以将其转换为更好的 XML。格式与我无关。我看到有很多这样的问题,但我发现这些例子很难适应,因为问题不是样式表不起作用而是它太慢了。

我的数据文件大小在 4-12 MB 之间。我在这里提供的 XSLT 可以很好地处理小文件。例如,当我将文件剪切为 250 KB 时,样式表可以很好地处理它(尽管这已经花费了大约 30 秒)。当我尝试处理实际较大的数据文件时,它似乎永远无法完成这项工作 - 即使只有一个文件也是如此。我有 Oxygen XML Editor,我一直在使用 Saxon-HE 9.5.1.2 进行转换。

备注:这仍然可能很慢。我可以让我的电脑过夜或做其他事情。这涉及一个格式错误的数据集,我根本不需要经常重复这种转换。

所以我的问题是:

这个 XSLT 中是否有什么东西让它运行得特别慢?其他方法会更好吗?

这些是简化的工作示例。实际的数据文件在结构上是相同的,但有更多的节点,在本例中我称之为“单词”。属性类型指定我要查找的节点。它是包含方言词及其规范化版本的语言方言数据。

这就是 XML。

<?xml version="1.0" encoding="UTF-8"?>
<xml>
<order>
    <slot id="ts1" value="1957"/>
    <slot id="ts2" value="1957"/>
    <slot id="ts3" value="2389"/>
    <slot id="ts4" value="2389"/>
    <slot id="ts5" value="2389"/>
    <slot id="ts6" value="2389"/>
    <slot id="ts7" value="3252"/>
    <slot id="ts8" value="3252"/>
    <slot id="ts9" value="3252"/>
    <slot id="ts10" value="3360"/>
</order>
<words type="original word">
    <annotation>
        <data id_1="ts1" id_2="ts3">
            <text>dialectal_word_1</text>
        </data>
    </annotation>
    <annotation>
        <data id_1="ts4" id_2="ts7">
            <text>dialectal_word_2</text>
        </data>
    </annotation>
    <annotation>
        <data id_1="ts8" id_2="ts10">
            <text>,</text>
        </data>
    </annotation>
</words>
<words type="normalized word">
    <annotation>
        <data id_1="ts2" id_2="ts5">
            <text>normalized_word_1</text>
        </data>
    </annotation>
    <annotation>
        <data id_1="ts6" id_2="ts9">
            <text>normalized_word_2</text>
        </data>
    </annotation>
</words>
</xml>
Run Code Online (Sandbox Code Playgroud)

这就是 XSLT。它尝试做的是选取 XML 结构中具有匹配值的对。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" encoding="UTF-8" indent="yes"/>
<xsl:template match="/xml">
    <xsl:text>original&#x9;normalized
</xsl:text>
        <xsl:for-each select="words[@type='original word']/annotation/data">
            <xsl:sort select="substring-after(@id_1, 'ts')" data-type="number"/>
            <xsl:variable name="origStartTimeId" select="@id_1"/>
            <xsl:variable name="origEndTimeId" select="@id_2"/>
            <xsl:variable name="origStartTime_VALUE" select="/xml/order/slot[@id=$origStartTimeId]/@value"/>
            <xsl:variable name="origEndTime_VALUE" select="/xml/order/slot[@id=$origEndTimeId]/@value"/>
                    <xsl:value-of select="text"/>
            <xsl:text>&#x9;</xsl:text>    
                <xsl:for-each select="/xml/words[@type='normalized word']/annotation/data">
                    <xsl:variable name="normStartTime" select="@id_1"/>
                    <xsl:variable name="normEndTime" select="@id_2"/>
                    <xsl:variable name="normStartTime_VALUE" select="/xml/order/slot[@id=$normStartTime]/@value"/>
                    <xsl:variable name="normEndTime_VALUE" select="/xml/order/slot[@id=$normEndTime]/@value"/>
                    <xsl:if test="($normStartTime_VALUE = $origStartTime_VALUE) and ($normEndTime_VALUE = $origEndTime_VALUE)">
                            <xsl:value-of select="text"/>    
                    </xsl:if>
                </xsl:for-each>
            <xsl:text>
</xsl:text>
        </xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

输出的内容很简单:

original    normalized
dialectal_word_1    normalized_word_1
dialectal_word_2    normalized_word_2
,   
Run Code Online (Sandbox Code Playgroud)

这对我来说很好。

谢谢!

Ian*_*rts 6

当前样式表中的双重嵌套 for-each 效率低下,并且随着文件大小的增长会变得更糟 - 你有(原始单词数)*(标准化单词数)迭代,本质上是二次复杂度(假设大约有文件中原始单词和标准化单词的数量相同)。如果您使用keys ,您可以做得更好,它通过构建一个查找表来工作,您可以使用该查找表非常快速地查找节点(通常在恒定时间而不是线性时间内)。

<!-- I've said version="2.0" to match your stylesheet in the question, but this
     code is actually valid XSLT 1.0 as it doesn't use any 2.0-specific features
     or functions -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="text" encoding="UTF-8" indent="yes"/>

  <!-- first key to look up slot elements by their id -->
  <xsl:key name="slotById" match="slot" use="@id" />
  <!-- second key to look up normalized word annotations by the value of their slots -->
  <xsl:key name="annotationBySlots" match="words[@type='normalized word']/annotation"
           use="concat(key('slotById', data/@id_1)/@value, '|',
                       key('slotById', data/@id_2)/@value)" />

  <xsl:template match="/xml">
    <xsl:text>original&#x9;normalized&#xA;</xsl:text>
    <xsl:apply-templates select="words[@type = 'original word']/annotation" />
  </xsl:template>

  <xsl:template match="annotation">
    <xsl:value-of select="data/text" />
    <xsl:text>&#x9;</xsl:text>
    <xsl:value-of select="
            key('annotationBySlots',
                concat(key('slotById', data/@id_1)/@value, '|',
                       key('slotById', data/@id_2)/@value)
            )/data/text" />
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

这应该以线性时间运行(每个原始单词注释一次“迭代”,加上构建查找表所花费的时间,该查找表再次应该与槽的数量加上标准化单词注释的数量成线性)。

  • 值得一提的是,某些处理器(例如 Saxon-EE)能够自动执行此优化。 (3认同)