Улучшение производительности XSL - PullRequest
3 голосов
/ 13 мая 2010

Я использую приведенный ниже код XSL 2.0, чтобы найти идентификаторы текстовых узлов, которые содержат список индексов, которые я даю в качестве входных данных. код работает отлично, но с точки зрения производительности он занимает много времени для огромных файлов. Даже для больших файлов, если значения индекса малы, результат будет быстрым за несколько мс. Я использую Java-процессор saxon9he для выполнения XSL.

<xsl:variable name="insert-data" as="element(data)*"> 
  <xsl:for-each-group 
    select="doc($insert-file)/insert-data/data" 
    group-by="xsd:integer(@index)"> 
    <xsl:sort select="current-grouping-key()"/> 
    <data 
      index="{current-grouping-key()}" 
      text-id="{generate-id(
        $main-root/descendant::text()[
          sum((preceding::text(), .)/string-length(.)) ge current-grouping-key()
        ][1]
      )}"> 
      <xsl:copy-of select="current-group()/node()"/> 
    </data> 
  </xsl:for-each-group> 
</xsl:variable> 

В приведенном выше решении, если значение индекса слишком велико, скажем, 270962, тогда время, необходимое для выполнения XSL, составляет 83427 мс. В больших файлах, если значение индекса велико, скажем, 4605415, 4605431, выполнение занимает несколько минут. Кажется, вычисление переменной «insert-data» занимает время, хотя это глобальная переменная и вычисляется только один раз. Должен ли XSL быть добавлен или процессор? Как я могу улучшить производительность XSL.

1 Ответ

2 голосов
/ 14 мая 2010

Я предполагаю, что проблема заключается в генерации text-id, то есть выражения

generate-id(
    $main-root/descendant::text()[
      sum((preceding::text(), .)/string-length(.)) ge current-grouping-key()
    ][1]
  )

Вы потенциально можете пересчитать много сумм здесь. Я думаю, что самый простой путь здесь - инвертировать ваш подход: рекурсивно проходить по текстовым узлам в документе, агрегировать длину строки до сих пор и выводить data элементов при каждом достижении нового @index. Следующий пример иллюстрирует подход. Обратите внимание, что каждый уникальный @index и каждый текстовый узел посещаются только один раз.

<xsl:variable name="insert-doc" select="doc($insert-file)"/>

<xsl:variable name="insert-data" as="element(data)*"> 
    <xsl:call-template name="calculate-data"/>
</xsl:variable>

<xsl:key name="index" match="data" use="xsd:integer(@index)"/>

<xsl:template name="calculate-data">
    <xsl:param name="text-nodes" select="$main-root//text()"/>
    <xsl:param name="previous-lengths" select="0"/>
    <xsl:param name="indexes" as="xsd:integer*">
        <xsl:perform-sort 
            select="distinct-values(
                    $insert-doc/insert-data/data/@index/xsd:integer(.))">
            <xsl:sort/>
        </xsl:perform-sort>
    </xsl:param>
    <xsl:if test="$text-nodes">
        <xsl:variable name="total-lengths" 
            select="$previous-lengths + string-length($text-nodes[1])"/>
        <xsl:choose>
            <xsl:when test="$total-lengths ge number($indexes[1])">
                <data 
                    index="{$indexes[1]}" 
                    text-id="{generate-id($text-nodes[1])}">
                    <xsl:copy-of select="key('index', $indexes[1], 
                                             $insert-doc)"/> 
                </data>
                <!-- Recursively move to the next index. -->
                <xsl:call-template name="calculate-data">
                    <xsl:with-param
                        name="text-nodes"
                        select="$text-nodes"/>
                    <xsl:with-param
                        name="previous-lengths" 
                        select="$previous-lengths"/>
                    <xsl:with-param
                        name="indexes" 
                        select="subsequence($indexes, 2)"/>
                </xsl:call-template>                    
            </xsl:when>
            <xsl:otherwise>
                <!-- Recursively move to the text node. -->
                <xsl:call-template name="calculate-data">
                    <xsl:with-param 
                        name="text-nodes" 
                        select="subsequence($text-nodes, 2)"/>
                    <xsl:with-param
                        name="previous-lengths" 
                        select="$total-lengths"/>
                    <xsl:with-param 
                        name="indexes" 
                        select="$indexes"/>
                </xsl:call-template>                    
            </xsl:otherwise>
        </xsl:choose>
    </xsl:if>
</xsl:template>
...