2008-10-22 20 views
9

Soy un ingeniero de soporte y el producto de nuestra compañía permite transformaciones XSLT para personalizar las salidas.XSLT Transform Efficiency

Hice una transformación xsl para este propósito. Funciona bien para archivos fuente de tamaño típico (varios 100k), pero de vez en cuando aparecerá un archivo fuente realmente enorme (10M). En tal caso, la salida no se genera, incluso si lo dejo afilar varios días.

El equipo de ingeniería de SW lo probó y descubrió que para la transformación y el archivo fuente grande en cuestión es de hecho muy lento (> días), si nuestro producto está compilado para usar el motor de transformación en .Net 1.1, pero si compilan con .Net 2.0, es bastante rápido (alrededor de 1-2 minutos).

La solución a largo plazo obviamente es, espere la próxima versión.

A corto plazo me pregunto lo siguiente: 1) ¿Es el XSLT lo suficientemente flexible como para que haya formas más eficientes y menos eficientes de lograr el mismo resultado? Por ejemplo, ¿es posible que la forma en que estructuré el xsl, el motor de transformación tenga que iterar muchas veces desde el principio del archivo fuente, tardando más y más a medida que la siguiente pieza de resultado se aleja cada vez más del principio? (Schlemiel el Pintor), o 2) ¿Es más dependiente de cómo el motor de transformación interpreta el xsl?

Si 2 es el caso, no quiero perder mucho tiempo tratando de mejorar el xsl (no soy un gran genio xsl, fue bastante difícil para mí lograr lo poco que hice ...)

Gracias!

+0

No había oído hablar de eso :) http://en.wikipedia.org/wiki/Schlemiel_the_painter%27s_Algorithm – wprl

+0

Lo escuché por primera vez en http://www.joelonsoftware.com/articles/fog0000000319.html que (bucles extraños) es como me enteré de StackOverflow – KnomDeGuerre

Respuesta

3

Para detectar cuándo comenzar una nueva sección, que hice esto:

<xsl:if test="@TheFirstCol>preceding-sibling::*[1]/@TheFirstCol" 

podría ser esto causa mucho o re-iteración?

Definitivamente. El algoritmo que ha elegido es O (N) y sería muy lento con un número suficiente de hermanos, independientemente del idioma de implementación.

Aquí es un algoritmo eficiente utilizando las teclas:

Solución 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> 

Desafortunadamente, XslTransform (.Net 1.1) tiene una aplicación notoriamente ineficiente de la función generate-id().

Lo siguiente puede ser más rápido con XslTransform:

Solución 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> 

cuando se aplica sobre la siguiente pequeña documento 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> 

ambas soluciones producen el resultado deseado:

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 

A partir del pequeño archivo XML anterior, generé un archivo XML de 10MB al copiar cada elemento 6250 veces (usando otra transformación XSLT :)).

Con el archivo 10 MB xml y con XslCompiledTransform (.Net 2.0 +) las dos soluciones tenían los siguientes tiempos de transformación:

Solution1: 3.3sec.
Solution2: 2.8sec.

Con XslTransform (.Net 1.1) Solution2 funcionó para 1622sec .; eso es aproximadamente 27 minutos.

+1

Acabo de ver que los y instrucciones no se mostraban - esto ahora se corrige. –

5

no estoy familiarizado con las implementaciones .NET, pero hay algunas cosas que puede hacer, en general, para acelerar el procesamiento de documentos de gran tamaño:

  • Evite utilizar "//" en expresiones XPath menos absolutamente necesario.
  • Si solo necesita el primer elemento o el único que coincida con una expresión Xpath, use el calificador "[1]", p. "// iframe [1]". Muchos procesadores implementan optimizaciones para esto.
  • Siempre que sea posible, cuando se trate de enormes entradas de XML, vea si puede diseñar una solución en torno a un analizador basado en secuencias (como SAX) en lugar de un analizador basado en DOM.
4

Normalmente, si ve un aumento no lineal en el tiempo de procesamiento frente al tamaño de entrada, debe sospechar su código más que el marco. Pero dado que el problema desaparece cuando la herramienta se compila con .NET 2.0, todas las apuestas están desactivadas.

Con XSLT, es difícil crear una curva de rendimiento no lineal si lo hace todo el análisis con fósforos plantilla rectas:

<xsl:template match="foo"> 
    <!--OUTPUT--> 
    <xsl:apply-templates/> 
    <!--OUTPUT--> 
</xsl:template> 

<xsl:template match="bar"> 
    <!--OUTPUT--> 
    <xsl:apply-templates/> 
    <!--OUTPUT--> 
</xsl:template> 

prestar especial atención a cualquier lugar que podría haber recurrido a <xsl:for-each> para el análisis ; las coincidencias de plantilla son virtualmente siempre una mejor manera de lograr el mismo resultado.

Una forma de solucionar este problema de rendimiento es recrear su XSLT una coincidencia de plantilla a la vez, probando el tiempo de procesamiento después de agregar cada coincidencia. Puede comenzar con esta coincidencia:

<xsl:template match="*"> 
    <xsl:copy>     <!--Copy node     --> 
    <xsl:copy-of select="@*"/> <!--Copy node attributes   --> 
    <xsl:apply-templates /> <!--Process children    --> 
    </xsl:copy> 
</xsl:template> 

Coincidirá y copiará cada nodo, uno a la vez, en un nuevo documento.Esto no debería mostrar un aumento no lineal en el tiempo de procesamiento frente al tamaño de entrada (si lo hace, entonces el problema no está en su código XSLT).

Al recrear su XSLT, si agrega una coincidencia de plantilla que de repente mata el rendimiento, comente cada bloque dentro de la plantilla. A continuación, descomente un bloque a la vez, probando el tiempo de procesamiento de cada iteración, hasta que encuentre el bloque que causa el problema.

2

Una cosa que se puede comprobar es si su XSLT busca mucho en otras partes del documento XML, es decir, se encuentra en un nodo de contexto y busca un valor en otra parte del documento o incluso en otro documento. Si lo hace, puede afectar el rendimiento bastante y debería considerar usar xsl:key y la función de tecla para esto. Le dice al procesador que implemente un índice de búsqueda rápida en los datos en cuestión.

Una vez construí un XSLT que tardó 8 horas en ejecutarse (con muchas referencias cruzadas) y el cambio a las teclas de uso le dio un aumento de velocidad enorme.

+0

¡Gracias! No tengo ninguna clave xsl: en mi xsl. – KnomDeGuerre

1

Al buscar su problema, encontré una KB en Microsoft sobre eso. Puedes verlo here.

Dicen que la transformación XSLT en .NET 1 tiene algunos problemas con el rendimiento y que pueden ofrecer una solución rápida.

Si quiere tratar de solucionar el problema, hay un perfilador XSLT disponible here.

De lo contrario, puede ver los enlaces que se proporcionan en el sitio web de Microsoft para optimizar los problemas de velocidad con XSLT (link).

+0

¡Gracias! No xsl: clave en mi transformación sin embargo. – KnomDeGuerre