XSLT: низкая производительность из-за сложного выражения XPath? - PullRequest
0 голосов
/ 11 декабря 2011

В какой-то момент в программе XSLT у меня есть следующее:

<xsl:for-each select="tags/tag">
    <xsl:apply-templates select="//shows/show[film=//films/film[tag=current()/@id]/@id]|//shows/show[group=//groups/group[film=//films/film[tag=current()/@id]/@id]/@id]">
        <xsl:sort select="date" data-type="text" order="ascending"/>
        <xsl:sort select="time" data-type="text" order="ascending"/>
        </xsl:apply-templates>
</xsl:for-each>

Кажется, что выражение XPath //shows/show[film=//films/film[tag=current()/@id]/@id]|//shows/show[group=//groups/group[film=//films/film[tag=current()/@id]/@id]/@id], которое является довольно сложным, значительно замедляет выполнение программы (по сравнениювремя выполнения перед добавлением цитируемого фрагмента кода - конечно, обработка тех же данных.

Считаете ли вы это нормальным из-за относительно сложной природы выражения, и вы видите, как яМожно ли улучшить его, чтобы он работал лучше?

Примечание: в выражении XPath film и //films/film, group и //groups/group относятся к разным элементам.

См. ниже aсокращенный образец XML-ввода.

<program>
  <tags>
    <tag id="1">Tag1</tag>
    <tag id="2">Tag2</tag>
    <tag id="3">Tag3</tag>
  </tags>
  <films>
    <film id="1">
        Film1
      <tag>2</tag><!-- References: /program/tags/tag/@id=2 -->
    </film>
    <film id="2">
        Film2
      <tag>1</tag><!-- References: /program/tags/tag/@id=1 -->
    </film>
    <film id="3">
        Film3
      <tag>3</tag><!-- References: /program/tags/tag/@id=3 -->
    </film>
    <film id="4">
        Film4
      <tag>3</tag><!-- References: /program/tags/tag/@id=3 -->
    </film>
  </film>
  <groups>
    <group id="1">
      <film>3</film><!-- References: /program/films/film/@id=3 -->
      <film>4</film><!-- References: /program/films/film/@id=4 -->
    </group>
  </groups>
  <shows>
    <show id="1"><!-- Show with film (=simple) -->
      <film>1</film><!-- References: /program/films/film/@id=1 -->
      <date>2011-12-12</date>
      <time>12:00</time>
    </show>
    <show id="2"><!-- Show with group (=combined) -->
      <group>1</group><!-- References: /program/groups/group/@id=1 -->
      <date>2011-12-12</date>
      <time>14:00</time>
    </show>
  </shows>
</program>

Пояснения:

  • Тег - это свойство, прикрепленное к фильму (на самом деле это скорее категория).
  • Группа - это перечень фильмов.
  • Шоу ссылается либо на фильм, либо на группу.
  • Что я хочу: для каждого тега я ищу ссылки на шоуфильм с текущим тегом и референц-шоуВ группе, где хотя бы один из фильмов имеет текущий тег.

Ответы [ 5 ]

6 голосов
/ 11 декабря 2011

Двойная косая черта в XPath - это производительность и загрузка ЦП при работе с большими документами (поскольку каждый узел в документе должен быть оценен).Если вы можете заменить его абсолютным или относительным путем, у вас должно быть заметное улучшение.Если вы можете опубликовать схему ввода и требуемый вывод, мы могли бы быть более конкретными?

например, с абсолютным путем

//shows/show[film=//films/film[tag=current()/@id]/@id]

становится

/myroot/somepath/shows/show[film=/myroot/somepath/films/film[tag=current()/@id]/@id]

или еслишоу и фильмы относятся к текущему узлу

./relativexpath/shows/show[film=./relativexpath/somepath/films/film[tag=current()/@id]/@id]
3 голосов
/ 11 декабря 2011

Ответ от nonnb, скорее всего, указывает на проблему, но не на эффективное решение («более дешевая» ось лучше, но сама по себе она не делает скорость такой, как при индексации данных).

Обратите внимание, что большая проблема заключается в том, что предикат выражения XPath делает еще один полный обход дерева для каждой оценки. Вы должны использовать ключи для подобных вещей; это (в большинстве или даже во всех реализациях XSLT) делает возможным индексированный поиск, тем самым значительно сокращая время выполнения.

Определение ключей для фильмов, групп и шоу по id:

<xsl:key name="filmByTag" match="film" use="tag" />
<xsl:key name="groupsByFilm" match="group" use="tag" />
<xsl:key name="showsByFilm" match="show" use="film" />
<xsl:key name="showsByGroup" match="show" use="group" />

А затем используйте его следующим образом (не проверено, но вы должны понять):

<xsl:variable name="films" select="key('filmByTag', @id)/@id" />
<xsl:apply-templates select="key('showsByFilm', $films)/@id|key('showsByGroups', key('groupsByFilm', $films)/@id)/@id">
2 голосов
/ 11 декабря 2011

Ваше выражение XPath, по-видимому, выполняет трехстороннее объединение, поэтому, если оно не оптимизировано, производительность, вероятно, составит O (n ^ 3) в размере исходного документа.Оптимизация включает замену последовательных поисков в документе индексированными поисками.Есть два способа добиться этого: вы можете оптимизировать его вручную, заменив выражения фильтра вызовами функции key () (как указано Dimitre), или вы можете использовать оптимизирующий процессор XSLT, такой как Saxon-EE, который долженсделать те же оптимизации автоматически.

1 голос
/ 11 декабря 2011

Вот два полных решения, которые должны демонстрировать лучшую производительность:

Примечание: 1004 *: лучшая производительность будет зарегистрирована только на достаточно больших входных выборках.На небольших входных выборках оптимизировать не стоит.

I.Не использовать // (но не использовать ключи)

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vFilms" select="/*/films/film"/>
 <xsl:variable name="vShows" select="/*/shows/show"/>
 <xsl:variable name="vGroups" select="/*/groups/group"/>
 <xsl:variable name="vTags" select="/*/tags/tag"/>

 <xsl:template match="/*">
     <xsl:for-each select="$vTags">
    <xsl:apply-templates select=
     "$vShows
              [film
              =
               $vFilms
                  [tag=current()/@id]
                         /@id

              or
                group
                =
                 $vGroups
                          [film
                          =
                           $vFilms
                              [tag=current()/@id]
                                       /@id
                           ]
                            /@id
               ]
      ">
        <xsl:sort select="date" data-type="text" order="ascending"/>
        <xsl:sort select="time" data-type="text" order="ascending"/>
        </xsl:apply-templates>
   </xsl:for-each>
 </xsl:template>

 <xsl:template match="show">
    <xsl:copy-of select="."/>
 </xsl:template>
</xsl:stylesheet>

II.Использование клавиш

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kShowByFilmId" match="show"
          use="film"/>

 <xsl:key name="kShowByGroupId" match="show"
          use="group"/>

 <xsl:key name="kGroupByFilmId" match="group"
          use="film"/>

 <xsl:key name="kFilmByTag" match="film"
          use="tag"/>

 <xsl:variable name="vTags" select="/*/tags/tag"/>

 <xsl:template match="/*">
     <xsl:for-each select="$vTags">

    <xsl:apply-templates select=
     "key('kShowByFilmId',
          key('kFilmByTag', current()/@id)/@id
          )
     |
      key('kShowByGroupId',
          key('kGroupByFilmId',
              key('kFilmByTag', current()/@id)/@id
              )
               /@id
         )
     ">
        <xsl:sort select="date" data-type="text" order="ascending"/>
        <xsl:sort select="time" data-type="text" order="ascending"/>
    </xsl:apply-templates>
   </xsl:for-each>
 </xsl:template>

 <xsl:template match="show">
    <xsl:copy-of select="."/>
 </xsl:template>
</xsl:stylesheet>
1 голос
/ 11 декабря 2011

Определите ключ с помощью xsl:key и затем используйте функцию key для перекрестной ссылки вместо того сравнения, которое у вас есть в данный момент. Покажите нам образец XML, чтобы мы могли понять его структуру, а затем мы можем помочь с конкретным кодом.

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