【问题标题】:I want to improve performance of xslt我想提高 xslt 的性能
【发布时间】:2011-01-19 03:34:10
【问题描述】:

我有一个基于 HTTP 请求和当前会话状态生成 XML 的框架。我可能会在 HTML 中进行测试,但生产输出将是 VXML——出于不同的原因,可能会使用一两种“风味”。

这是我的 HttpServlet 的慢速部分:

jsp InputStream ms = new java.io.ByteArrayInputStream(sb.toString().getBytes());
Source xmlSource = new javax.xml.transform.stream.StreamSource(ms);
String filePath = getServletContext().getRealPath(("/GetNextEvent-").
        concat(req.getSession().getAttribute("client").toString().toUpperCase()).concat(".xsl"));
Source xsltSource = new javax.xml.transform.stream.StreamSource(filePath);
Result result = new javax.xml.transform.stream.StreamResult(resp.getWriter());
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer(xsltSource);
t.transform(xmlSource, result);

目前这需要大约 200 毫秒。我希望它更快。可能

  1. 关于缓存的建议? - 看到 xsl 文件在整个部署过程中保持不变,可以无限期地缓存 Transformer 对象。我正在考虑在会话级别缓存它,所以每个会话(1000 个同时)都有自己的。有什么建议?出于任何原因,我应该使用任何框架进行缓存吗?
  2. 有没有更快的方法将 xml 转换为响应流?
  3. 我应该放弃这个并走另一条路吗?如果您注意到 sb.toString,我正在使用 StringBuilder 来获取对象的 XML 表示(对象使用 stringbuilder 来创建 XML 字符串)。使用 StringBuilders 创建 XML 文档大约需要 1 毫秒,所以我暂时不关心它。

编辑:

这是 XSL 文档。 XML 文档通常非常小。只是几个元素。 XML 示例位于 XSL 下方:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:regexp="http://exslt.org/regular-expressions"
    xmlns:str="http://exslt.org/strings" xmlns:twc="http://twc.com/2009/01/ivr/framework"
    exclude-result-prefixes="twc regexp str" extension-element-prefixes="str">
    <xsl:output method="xml" encoding="ISO-8859-1" />
    <xsl:template match="/">
        <vxml xmlns="http://www.w3.org/2001/vxml" version="2.1" xml:lang="en-US"
            application="root.xml">
            <xsl:attribute name="xml:lang"><xsl:value-of
                select="//twc:response/@language" /></xsl:attribute>
            <form id="ivrFramework">
                <var name="logDebug">
                    <xsl:attribute name="expr"><xsl:value-of
                        select="//twc:response/@debug" /></xsl:attribute>
                </var>
                <var name="event" expr="'OK'" />
                <var name="lastResult" expr="''" />
                <var name="lastResultMode" expr="''" />
                <var name="lastResultValue" expr="''" />
                <var name="srConfidence" expr="'1000'" />

                <xsl:apply-templates select="//twc:command" />
                <xsl:if test="count(//twc:command)=0">
                    <block>
                        <log cond="logDebug" expr="'No more commands.  Exiting.'" />
                        <exit />
                    </block>
                </xsl:if>
            </form>
        </vxml>
    </xsl:template>

    <xsl:template
        match="twc:command[@type='prompt' and contains(text(), 'TransferDialog')]">
        <transfer name="quicktransfer"  type="consultation">
            <xsl:attribute name="destexpr"><xsl:choose>
                <xsl:when test="//twc:parameter[twc:name='destination']">'<xsl:value-of
                select="//twc:parameter[twc:name='destination']/twc:value" />'</xsl:when>
                <xsl:otherwise>'tel:1136300'</xsl:otherwise>
            </xsl:choose>
            </xsl:attribute>
            <xsl:if test="//twc:parameter[twc:name='initial']">
                <prompt>
                    <xsl:call-template name="process_prompt">
                        <xsl:with-param name="prompt_type" select="'initial'" />
                    </xsl:call-template>
                </prompt>
            </xsl:if>
        </transfer>
    </xsl:template>

    <xsl:template
        match="twc:command[@type='prompt' and contains(text(), 'BasicDialog')]">
        <xsl:choose>
            <xsl:when test="//twc:parameter[twc:name='grammar']/twc:value">
                <field>
                    <xsl:attribute name="name"><xsl:value-of
                        select="//twc:parameter[twc:name='variable']/twc:value" /></xsl:attribute>
                    <noinput count="3">
                        <assign name="event" expr="'noinput'" />
                        <submit next="GetNextEvent2.jsp"
                            namelist="event lastResult lastResultMode lastResultValue srConfidence" />
                    </noinput>
                    <nomatch count="3">
                        <assign name="event" expr="'invalid'" />
                        <submit next="GetNextEvent2.jsp"
                            namelist="event lastResult lastResultMode lastResultValue srConfidence" />
                    </nomatch>

                    <xsl:for-each select="//twc:parameter[twc:name='grammar']/twc:value">
                        <grammar>
                            <xsl:attribute name="src"><xsl:if test="//twc:response/@base!=''"><xsl:value-of select="//twc:response/@base" /></xsl:if><xsl:value-of
                                select="." /></xsl:attribute>
                        </grammar>
                    </xsl:for-each>
                    <xsl:if test="//twc:parameter[twc:name='help']">
                        <help>
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'help'" />
                            </xsl:call-template>
                        </help>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='noinput1']">
                        <noinput count="1">
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'noinput1'" />
                            </xsl:call-template>
                        </noinput>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='noinput2']">
                        <noinput count="2">
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'noinput2'" />
                            </xsl:call-template>
                        </noinput>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='invalid1']">
                        <nomatch count="1">
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'invalid1'" />
                            </xsl:call-template>
                        </nomatch>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='invalid2']">
                        <nomatch count="2">
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'invalid2'" />
                            </xsl:call-template>
                        </nomatch>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='initial']">
                        <prompt>
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'initial'" />
                            </xsl:call-template>
                        </prompt>
                    </xsl:if>
                    <filled>
                        <log cond="logDebug" expr="'Filled.'" />
                        <assign name="event" expr="'OK'" />
                        <assign name="lastResult" expr="application.lastresult$.utterance" />
                        <assign name="lastResultMode" expr="application.lastresult$.inputmode" />
                        <assign name="lastResultValue" expr="application.lastresult$.interpretation" />
                        <assign name="srConfidence" expr="application.lastresult$.confidence " />
                        <submit next="GetNextEvent2.jsp"
                            namelist="event lastResult lastResultMode lastResultValue srConfidence" />
                    </filled>

                </field>
            </xsl:when>
            <xsl:when test="//twc:parameter[twc:name='initial']/twc:value">
                <block>
                    <xsl:if test="//twc:parameter[twc:name='initial']">
                        <prompt>
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'initial'" />
                            </xsl:call-template>
                        </prompt>
                    </xsl:if>
                    <submit next="GetNextEvent2.jsp"
                        namelist="event lastResult lastResultMode lastResultValue srConfidence" />
                </block>
            </xsl:when>
            <xsl:otherwise>
                <block>
                    <log cond="logDebug" expr="'Didn't find values for grammar or initial.  Exiting.'" />
                    <exit />
                </block>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="process_prompt">
        <xsl:param name="prompt_type" />
        <xsl:for-each select="//twc:parameter[twc:name=$prompt_type]/twc:value">
            <xsl:if test="contains(., '::')">
                <audio>
                    <xsl:for-each select="str:split(., '::')">
                        <xsl:if test="position()=1">
                            <xsl:attribute name="src"><xsl:if test="//twc:response/@base!=''"><xsl:value-of select="//twc:response/@base" /></xsl:if><xsl:value-of
                                select="." /></xsl:attribute>
                        </xsl:if>
                        <xsl:if test="position()=2">
                            <xsl:value-of select="." />
                        </xsl:if>
                    </xsl:for-each>
                </audio>
            </xsl:if>
            <xsl:if test="contains(., 'Date:')">
                <say-as interpret-as="date" format="ymd">
                    <xsl:for-each select="str:split(., ':')">
                        <xsl:if test="position()=2">
                            <xsl:value-of select="." />
                        </xsl:if>
                    </xsl:for-each>
                </say-as>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

这是一些 XML:

<?xml version="1.0"?>
<response xmlns="http://twc.com/2009/01/ivr/framework" language="en-us" debug="true"
    base="/IVRFrameworkResources/Outage/">
    <command type="prompt"> BasicDialog <parameter>
            <name>initial</name>
            <value>en-us/prompts/OutageCleared.wav::Hello.  I'm letting you know the
                incident that caused your outage has been fixed. </value>
        </parameter>
    </command>
</response>

【问题讨论】:

  • 您能否向我们展示您的 XSLT 以及示例输入文档?您的 XSLT 可能包含可以优化的低效查询(顺便说一句,几乎可以用任何查询语言创建低效查询,因此指责 XLST 速度慢通常是不公平的)。
  • 我将附加 XSLT,但也许这可能是另一个问题:)
  • 你也可以显示input文档吗?这比输出更重要。
  • 提供的 XSLT 代码证实了它远非最佳的怀疑。它很可能也不正确。提供的源 XML 文档不具有代表性,因为它是微不足道的,并且根本不允许执行任何重要的代码。需要一个更具代表性的示例以及所解决问题的定义——xml 文档的含义是什么以及必须产生什么结果。到目前为止,使用“//”可能会导致速度大幅下降,而且很可能是不正确的。
  • @Dimitre - 目前,XML 文档就是这么简单。它们可以变得更复杂一些,但不会更大。不过,我将清理 XSL!感谢大家让我意识到这一点。

标签: java jsp xslt


【解决方案1】:

如果不查看 XSLT 或不知道 XML 和 XSLT 的大小/复杂程度,就很难诊断性能问题。

您可能需要支付解析文件(XSLT 或 XML)的费用,并且/或者您可能拥有一个非常低效的 XSLT 样式表。

例如:

  1. 大量的// XPATH 语句,如果不需要,可能会损害非常大的 XML 文件的性能。
  2. 逻辑隐藏在模板中,可以向上移动到模板@match 标准中,这为XSLT 引擎提供了优化的机会。

您可以使用 XSLT 分析器来查看 XSLT 中的瓶颈所在。例如,oXygen 有一个很好的debugger/profiler

如果您将多次运行 XSLT,那么 您应该 cache the transformer object 。这样,您只需支付加载和实例化一次并重复使用多次的成本。

例如,将 XSLT Template 对象的实例化移动到您的 severlet init()

 TransformerFactory transFact = TransformerFactory.newInstance();
 Templates cachedXSLT = transFact.newTemplates(xsltSource);

然后在你执行转换的地方,使用缓存的TransformerFactory obj:

Transformer t= cachedXSLT.newTransformer();
t.transform(xmlSource, result);

【讨论】:

  • +1 用于使用模板。如果使用 Xalan 作为 XSLT 转换器,请尝试使用 XSLTC 版本,它编译样式表并且速度更快。请参阅xml.apache.org/xalan-j/xsltc_usage.html#api 如果使用 Xalan,我会检查所使用的 DOM,在某些情况下,TinyTree 与普通 Java DOM 相比具有一些主要优势(结果可能会有所不同,但 TinyTree 的内存使用情况要好得多)。
【解决方案2】:

即使使用缓存,性能不可接受的原因通常在于 XSLT 代码本身——您根本没有展示出来。

根据我的经验,曾有过这样的案例,我能够以这样一种方式更改效率低下的 XSLT 实现,从而将其加速数千倍。

当存在 O(N) 甚至 O(log(N)) 算法时,作者经常实现 O(N^2) 算法或更糟。

向我们说明要解决的问题并提供解决问题的 XSLT 代码。那么也许有人可以为您提供性能更好的解决方案

【讨论】:

    【解决方案3】:

    我遇到了一些将 XSLT 文件转换为 java 解析代码的工具,但现在找不到任何参考。抱歉,答案不完整,但我也很高兴找到它。

    【讨论】:

    • 这个工具是 Saxon 并且 Michael Kay 只是询问了 saxon-help 列表是否有人需要它,以便他知道是否继续维护它。在那里发表您的回复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多