2008-10-22 74 views
9

我是支持工程師,我們公司的產品允許XSLT轉換來定製輸出。XSLT轉換效率

我爲此做了一個xsl轉換。它適用於典型大小(幾十萬)的源文件,但偶爾會出現一個真正巨大的(10M)源文件。在這種情況下,即使我讓它磨了幾天也不會產生輸出。

如果我們的產品被編譯爲使用.Net 1.1中的轉換引擎,但SW編譯團隊測試了它並發現對於轉換和大型源文件而言確實非常慢(>天)它與.Net 2.0,它是快速(大約1-2分鐘)。

長期的解決方案顯然是等待下一個版本。

對於短期我想知道以下內容: 1)XSLT是否足夠靈活,以至於有更高效和效率更低的方法來達到相同的結果?例如,是否有可能我構建xsl的方式,轉換引擎必須從源文件的開始多次迭代,需要更長和更長的時間,因爲下一個結果塊從一開始就變得越來越遠? (Schlemiel the Painter)或 2)它是否更依賴於轉換引擎如何解釋xsl?

如果是2的話,我不想浪費大量時間來改善xsl(我不是一個很大的xsl天才,我很難完成我所做的一切...... )。

謝謝!

+0

我還沒有聽說過那個:) http://en.wikipedia.org/wiki/Schlemiel_the_painter%27s_Algorithm – wprl 2008-10-22 19:19:50

+0

我第一次聽到它在http://www.joelonsoftware.com/articles/fog0000000319.html 哪個(奇怪的循環)是我怎麼發現關於StackOverflow – KnomDeGuerre 2008-10-22 20:03:54

回答

3

要檢測什麼時候開始一個新的部分,我這樣做:

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

難道這是造成很多或再重複?

當然。您選擇的算法是O(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> 

不幸的是,的XslTransform(.NET 1.1)具有衆所周知的低效率的實施generate-id()功能。

以下可以是具有的XslTransform更快:

溶液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> 

當在以下的小的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> 

兩種溶液所產生的所需的結果:

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 

從上面的小XML文件我通過複製每個元素6250次(使用另一個XSLT轉換:))生成一個10MB XML文件。

隨着10MB xml文件,並用XslCompiledTransform(.NET 2.0 +)兩種溶液具有下列變換次數:

解決方法1:3.3sec。
解決方案2:2.8秒。

使用XslTransform(.Net 1.1)解決方案2運行1622sec。即大約27分鐘。

5

我不熟悉的.NET實現,但也有一些事情你可以做一般要加快大型文件處理:

  • 避免在XPath表達式中使用「//」,除非絕對有必要。
  • 如果您只需要匹配Xpath表達式的第一個或唯一元素,請使用「[1]」限定符,例如「// iframe中[1]」。許多處理器爲此實現優化。
  • 只要有可能,在處理大量XML輸入時,看看您是否可以圍繞基於流的解析器(如SAX)而不是基於DOM的解析器設計解決方案。
4

通常情況下,如果您看到處理時間與輸入大小的非線性增加,那麼您應該懷疑自己的代碼比框架多。但是,由於在.NET 2.0編譯工具時問題消失,所有投注都關閉。

使用XSLT,這是努力創造一個非線性性能曲線,如果你做你的分析與直模板:

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

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

密切注意任何地方,你可能已經使出<xsl:for-each>解析;模板匹配幾乎總是獲得相同結果的更好方法。

解決此性能問題的一種方法是一次重新創建您的XSLT模板匹配,在添加每個匹配項後測試處理時間。您可以從此匹配開始:

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

這將匹配並逐個複製每個節點到一個新文檔。這應該不會在處理時間與輸入大小之間呈現非線性增加(如果是這樣,那麼問題不在於您的XSLT代碼)。

當您重新創建XSLT時,如果添加模板匹配突然導致性能下降,請將模板中的每個塊註釋掉。然後,每次取消一個塊的註釋,測試每次迭代的處理時間,直到找到導致問題的塊。

2

世界檢查的一件事是,如果您的XSLT對XML文檔的其他部分進行了很多查找,即您處於一個上下文節點並在文檔的另一部分甚至另一個文檔中查找值。如果你這樣做,它可能會很難達到性能,你應該考慮使用xsl:key和這個關鍵功能。它告訴處理器在所討論的數據上實現快速查找索引。

我曾經構建過需要8個小時才能運行的XSLT(有很多交叉引用),並且切換到使用鍵可以大大提高速度。

+0

謝謝!我的xsl中沒有任何xsl:鍵。 – KnomDeGuerre 2008-10-24 22:57:15

1

查找到您的問題後,我在Microsoft找到了一個關於該問題的知識庫。你可以看到它here

他們說.NET 1中的XSLT轉換在性能方面存在一些問題,並且他們可以提供快速修復。

如果您想嘗試解決問題,則可以使用XSLT分析器here

否則,您可以看到Microsoft網站上提供了哪些鏈接,用於優化XSLT的速度問題(link)。

+0

謝謝!儘管我的轉換中沒有xsl:鍵。 – KnomDeGuerre 2008-10-24 22:58:14