Я хочу улучшить производительность xslt - PullRequest
9 голосов
/ 20 февраля 2010

У меня есть структура, которая генерирует XML на основе HTTP-запроса и текущего состояния сеанса. Я могу протестировать в 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 мс. Я бы хотел, чтобы это было намного быстрее. Возможно, <10 мс? </p>

  1. Предложения по кешированию? - видя, что файлы xsl остаются неизменными на протяжении всего развертывания, объекты Transformer могут кэшироваться бесконечно. Я думаю о кешировании на уровне сеанса, поэтому каждый сеанс (1000 сеансов) имеет свой. Какие-либо предложения? Должен ли я использовать какие-либо рамки для кэширования, по любой причине?
  2. Есть ли более быстрый способ преобразования xml в поток ответов?
  3. Должен ли я отказаться от этого и пойти другим путем? Если вы заметили sb.toString, я использую StringBuilder для получения XML-представления объектов (объекты используют строитель строк для создания строки XML). Создание XML-документа с использованием StringBuilders занимает около 1 миллисекунды, поэтому в данный момент меня это не беспокоит.

Edit:

Вот документ 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>

Ответы [ 3 ]

12 голосов
/ 20 февраля 2010

Трудно диагностировать проблемы производительности, не видя XSLT или не зная, насколько велики / сложны XML и XSLT.

Возможно, вы платите за анализ файла (ов), XSLT или XML, и / или у вас может быть очень неэффективная таблица стилей XSLT.

Например:

  1. Множество // операторов XPATH, которые, если не нужны, могут снизить производительность для очень больших файлов XML.
  2. Логика скрыта внутри шаблонов , которые могут быть перемещены в критерии @match шаблона, что дает возможность оптимизировать движки XSLT.

Существуют профилировщики XSLT, которые вы можете использовать для определения узких мест в вашем XSLT. Например, oXygen имеет очень хороший отладчик / профилировщик : alt text alt text

Если вы будете многократно запускать XSLT, тогда вам следует кэшировать объект преобразователя . Таким образом, вы платите только за загрузку и создание экземпляра один раз и многократное использование.

Например, переместите экземпляр вашего объекта XSLT Template в свой severlet init()

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

и затем, где вы выполняете преобразование, используйте кешированный TransformerFactory obj:

Transformer t= cachedXSLT.newTransformer();
t.transform(xmlSource, result);
5 голосов
/ 20 февраля 2010

Даже при кэшировании причина неприемлемой производительности часто заключается в самом коде XSLT - который вы вообще не показывали.

По моему опыту, были случаи, когда мне удавалось изменить неэффективную реализацию XSLT таким образом, чтобы она была ускорена в тысячи раз.

Довольно часто автор реализует алгоритм O (N ^ 2) или хуже, когда существуют алгоритмы O (N) или даже O (log (N)).

Укажите нам решаемую проблему и предоставьте код XSLT, который ее решает. Тогда кто-то может предложить вам более эффективное решение .

0 голосов
/ 20 февраля 2010

Я встречал какой-то инструмент, который конвертирует XSLT-файл в код синтаксического анализа Java, но сейчас не могу найти никаких ссылок. Извините за неполный ответ, но мне тоже интересно его найти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...