XSLT工作太慢
我有大约100个XML文件,我想要转换成更好的结构的另一个文件。此示例将其转换为CSV,但我也有一个将其转换为更好的XML的变体。格式与我无关。我发现这里有很多问题,但是我发现这些例子很难适应,因为问题不在于样式表不起作用,而是太慢。XSLT工作太慢
我的数据文件的大小在4-12 MB之间。我在这里提供的XSLT适用于小文件。作为一个例子,当我将一个文件剪切为250 KB的时候,样式表处理得很好(尽管这需要大约30秒)。当我尝试使用实际更大的数据文件时,它似乎从未完成这项工作 - 即使只有一个文件。我有氧气XML编辑器,我一直在使用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>
这是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	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>	</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>
什么是输出很简单:
original normalized
dialectal_word_1 normalized_word_1
dialectal_word_2 normalized_word_2
,
而这会为我被罚款。
谢谢!
在当前样式表中双重嵌套for-each效率不高,随着文件大小的增长会变得更糟 - 您得到了(原始字数)*(标准化字数)迭代,基本上是二次型复杂度(假设文件中的原始和标准化字数大致相同)。如果你使用键,你可以做得更好,它通过建立一个查找表来工作,你可以用它来非常快地找到节点(通常在恒定的时间而不是线性的时间)。
<!-- 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	normalized
</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>	</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>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
这应该以线性时间运行每原始字注释(一个“迭代”,加上建立这又应在时隙的数目线性加的归一化数量的查找表所花费的时间单词注释)。
构造像/xml/order/slot[@id=$origStartTimeId]
要求定义一个键<xsl:key name="slot-by-id" match="xml/order/slot" use="@id"/>
,然后使用key('slot-by-id', $origStartTimeId)
而不是/xml/order/slot[@id=$origStartTimeId]
。在所有地方做出同样的改变,我相信表演会增加。
这很好,谢谢你的解释呢!只是为了报告结果:处理时间与最大的文件之间的时间差距不到一秒,可能还有几秒钟。 – nikopartanen 2014-10-03 11:19:03
可能值得一提的是,一些处理器(Saxon-EE就是一个例子)能够自动执行此优化。 – 2014-10-03 14:15:38