Migliorare le prestazioni di XSL
Domanda
Sto usando il codice XSL 2.0 di seguito per trovare gli ID dei nodi di testo che contiene l'elenco degli indici che do come input. Il codice funziona perfettamente ma in termini di prestazioni impiega molto tempo per file enormi. Anche per file enormi se i valori dell'indice sono piccoli, quindi il risultato è rapido in pochi ms. Sto usando il processore Saxon9He Java per eseguire 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>
Nella soluzione sopra se il valore dell'indice è troppo enorme, diciamo 270962, il tempo impiegato per l'esecuzione di XSL è di 83427ms. In file enormi se il valore dell'indice è enorme per esempio 4605415, 4605431 ci vogliono diversi minuti per essere eseguiti. Sembra che il calcolo della variabile "Insert-Data" richiede tempo sebbene sia una variabile globale e calcolata una sola volta. L'XSL dovrebbe essere aggiunto o il processore? Come posso migliorare le prestazioni dell'XSL.
Soluzione
Immagino che il problema sia la generazione di text-id
, cioè l'espressione
generate-id( $main-root/descendant::text()[ sum((preceding::text(), .)/string-length(.)) ge current-grouping-key() ][1] )
Stai potenzialmente ricalcolando molte somme qui. Penso che il percorso più semplice qui sarebbe quello di invertire il tuo approccio: ricorrere attraverso i nodi di testo nel documento, aggregare la lunghezza della stringa finora e output data
elementi ogni volta che un nuovo @index
è raggiunto. Il seguente esempio illustra l'approccio. Nota che ogni unico @index
e ogni nodo di testo viene visitato una sola volta.
<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>