我希望本文有多种用途。 首先,我希望它能使样式表作者对他们可以期望XSLT处理器进行什么样的优化感到,并暗示一些当前尚未优化的结构。 当然,此类优化的细节因处理器而异,因处理器而异,但我希望阅读此帐户可以使您对幕后工作有更好的了解。 其次,它描述了我认为XSLT技术的最新水平(在这方面,我认为Saxon根本没有比其他XSLT处理器先进或更低),并描述了我认为还有进一步发展空间的领域技术发展。 我希望这种描述可能会激发在编译器和数据库优化方面具有丰富经验的研究人员在该领域的进一步工作。

最后(最后也是最不重要的一点),本文旨在作为任何想要研究Saxon源代码的人的起点。 它不是作为代码之旅而写的,也不是假设您想进入那种深度。 但是,如果您有兴趣获得更高层次的概述,而不是深入研究JavaDoc规范或源代码本身,则可能会发现这很有用。

有两个警告:我描述的版本是Saxon 6.1,我描述了代码的功能分解,这些分解并不总是清楚地映射到包和模块结构。 例如,本文将编译器和解释器描述为单独的功能组件。 但是在实际代码中,例如,处理<xsl:choose>指令的模块同时包含编译时代码和运行时代码以支持该指令。 如果您确实想将本文用作代码指南,我会偶尔包含对包和模块名称的引用,以便您了解在哪里查找。

首先,我将描述Saxon产品的设计。 Saxon是XSLT处理器。 即,该程序将XML文档和样式表作为输入,并生成结果文档作为输出。 (我假设您对XSLT有所了解,但是如果您不熟悉XSLT,则可能会发现我的配套文章作为介绍很有用。)

Saxon包括最初由David Megginson编写的开源AElfred XML解析器的副本,尽管它可以与实现Java SAX接口的任何其他XML解析器一起使用。 Saxon还包括一个序列化程序,该序列化程序将结果树转换为XML,HTML或纯文本。 从技术上讲,串行器不是XSLT处理器的一部分,但对于实际使用而言,它是必不可少的。

Saxon实现了TrAX(XML转换API)接口,该接口定义为JAXP 1.1 Java扩展的一部分。 您无需了解此接口即可欣赏本文,但了解TrAX的体系结构将帮助您了解Saxon的结构方式。

撒克逊建筑

Saxon软件的主要组件如图1所示。

图1. Saxon体系结构

树构造函数创建源XML文档的树表示形式。 它用于处理源文档和样式表。 这有两个部分:

  • XML解析器 (包com.icl.saxon.aelfred )读取源文档并通知事件,例如元素的开始和结束。
  • 这些事件将通知树构建器 (模块com.icl.saxon.Builder ),并使用它们来构造XML文档的内存表示形式。

解析器和构建器之间的接口是Java SAX2 API。 尽管此SAX API只是非正式的标准化,但它由六个免费的XML解析器实现,从而允许Saxon与这些解析器中的任何一个互换使用。 在解析器和树生成器之间放置着一个我称为Stripper器的组件(我无法拒绝这个名称):根据<xsl:preserve-space> Stripper执行删除空格文本节点的功能,然后将它们添加到树中。样式表(模块com.icl.saxon.Stripper )中的com.icl.saxon.Stripper <xsl:preserve-space><xsl:strip-space>指令。 Stripper是SAX筛选Stripper的一个很好的示例,该代码段将SAX事件流作为输入,并生成另一SAX事件流作为输出。 在更宏观的层面上,整个Saxon转换也可以作为SAX过滤器进行操作。 这种方法非常容易将复杂的转换分解为排列在管道中的一系列简单的转换。

顾名思义, 树导航器允许应用程序通过在层次结构中导航来从树中选择节点。 由构建器组件构造的树表示是Saxon专有的。 这是Saxon与其他XSLT处理器不同的地方:其中一些使用通用DOM模型作为内部树。 使用DOM的好处是,树可以由第三方软件生成。 可以将为不同目的构造的树直接作为转换的输入提供,同样,基于DOM的应用程序可以直接使用转换的输出。

在Saxon中,我认为使用DOM提供的互操作性成本太高。 首先,DOM树模型与XSLT处理器所需的XPath模型有细微的不同,这种差异在将一个模型映射到另一个模型时增加了运行时成本。 例如,DOM树可以包含XPath模型不需要的信息,例如实体节点。 其次,可以在适当位置更新DOM树,而XSLT处理模型意味着仅顺序写入树。 设计只能顺序写入的树模型可以提高效率。 例如,每个节点都可以包含一个序列号,这使得按顺序的文档顺序对节点进行排序变得很容易,这是XSLT的一项常见要求。 最后,DOM实现通常包含许多同步代码以使多线程访问安全。 因为XSLT处理模型是“一次写入,多次读取”,所以同步逻辑可以更简单,从而可以更快地导航树。

实际上,正如您将看到的,Saxon提供了两种不同的树实现,每种实现都有自己的构建器和导航类(包com.icl.saxon.treecom.icl.saxon.tinytree )。 两种实现方式提供了不同的性能折衷。

样式表编译器在执行之前分析样式表。 它不产生可执行代码; 它生成样式表的装饰树表示 ,在其中验证和解析所有XPath表达式,解决所有交叉引用,预分配堆栈框架插槽,依此类推。 样式表编译器因此执行了重要的功能,即构造决策树以在执行时使用,以找到正确的模板规则来处理每个输入节点; 尝试将每个节点与每个可能的模式进行匹配将非常低效。 然后,装饰树在转换时起作用,以驱动样式表处理。 (编译器分布在com.icl.saxon.style包中的各个类中,尤其是方法prepareAttributes()preprocess()validate() )。

在一个阶段,Saxon实际上包括一个样式表编译器,该编译器生成可执行的Java代码。 但是,它仅处理XSLT语言的一个子集,并且随着技术的发展,通过完全编译获得的性能收益正在减少。 最终,随着开发复杂性的增加而性能收益的下降,我放弃了这种方法。 当前市场上没有完整的XSLT编译器。 Sun已经制作了一个名为XSLTC编译器,看起来很有希望(见的alpha版本相关信息 ),虽然它仍然处于发展的早期阶段。

无法将Saxon的样式表编译器生成的装饰树(植根于com.icl.saxon.style.XSLStyleSheet类)保存到磁盘,因为将树读回内存比重新编译原始树花的时间更长(很大程度上是因为其增大了大小) 。 只要树保留在内存中,就可以重用它。 该树包装在名为PreparedStyleSheet的对象中,该对象在JAXP 1.1中实现javax.xml.transform.Templates接口。 在服务器环境中,重复使用同一样式表来转换许多不同的源文档是很常见的。 为此,已编译的样式表在执行时严格是只读的,从而可以在多个执行线程中同时使用它。

Saxon处理器的核心是样式表解释器 ( com.icl.saxon.Controller类,该类在JAXP 1.1中实现javax.xml.transform.Transformer接口)。 该解释器使用修饰的样式表树来驱动处理。 按照语言的处理模型,它首先找到用于处理输入树的根节点的模板规则。 然后,它评估该模板规则(或在标准术语中被“实例化”)。

样式表树使用不同的Java类来表示每种XSL指令类型。 例如,考虑以下说明:

<xsl:if test="parent::section"><h3><xsl:value-of select="../@title"/></h3>
</xsl:if>

如果源树上的当前节点的父元素类型为<section> ,则此代码片段的作用是将<h3>元素输出到结果树; 生成的<h3>节点的文本内容是父sectiontitle属性的值。

此代码片段在装饰的样式表树上由图2所示的结构表示。

图2.装饰的样式表树

样式表中的元素直接映射到树上的节点,如表1所示。 所有表示元素的Java对象都是com.icl.saxon.style.StyleElement的子类, com.icl.saxon.tree.ElementImpl的子类是Saxon树结构中元素节点的默认实现。 这两个XPath表达式由从树的节点引用的com.icl.saxon.expr.Expression对象表示。

表1.样式表元素及其对应的Java类

元素或表达式... ...由此Java类中的对象表示com.icl.saxon.style.StyleElement子类)
<xsl:if> com.icl.saxon.style.XSLIf
<h3> (输出,不是指令) com.icl.saxon.style.LiteralResultElement
<xsl:value-of> com.icl.saxon.style.XSLValueOf
XPath表达式 com.icl.saxon.expr.Expression

执行<xsl:if>指令将导致执行相应XSLIf对象的process()方法。 此方法访问测试Expression对象,该对象具有方法evaluateAsBoolean()evaluateAsBoolean()用于对表达式求值以返回布尔结果。 (这是一种优化:如规范中所述,可以使用直接的evaluate()调用,然后将结果转换为布尔值。但是知道布尔值通常可以实现更快的评估。例如,当实际值或表达式是节点集,则一旦找到该节点集的单个成员,就会知道最终的布尔结果)。

如果evaluateAsBoolean()的结果为true,则process()方法将调用样式表树上XSLIf节点的所有子节点的process()方法。 如果结果不正确,则直接退出。

同样, LiteralResultElementprocess()方法将元素复制到结果树并处理LiteralResultElement的子LiteralResultElement ,而XSLValueOf对象的process()方法将选择表达式评估为字符串,并将结果复制为文本结果树的节点。

因此,样式表解释器的关键组件是:

  • 每种样式表指令类型的类,包含该指令的逻辑
  • 一组支持类,用于处理变量的绑定,运行时上下文的管理以及节点与模板规则的匹配
  • XPath表达式解释器,用于评估XPath表达式并返回其值

XPath解释器(软件包com.icl.saxon.expr )紧密遵循Interpreter设计模式,该模式是Gamma,Helm,Johnson和Vlissides描述的23种面向对象软件的经典模式之一。 XPath语法中的每个构造都有一个对应的Java类。 例如, UnionExpr构造(写为A|B ,代表两个节点集的并集)由com.icl.saxon.expr.UnionExpressioncom.icl.saxon.expr.UnionExpression 。 XPath表达式解析器(模块com.icl.saxon.expr.ExpressionParser )通常在编译样式表时执行,生成一个数据结构,该数据结构直接反映表达式的解析树。 为了评估表达式,此结构中的每个类都有一个evaluate()方法,该方法负责返回其值。 在UnionExpression类的情况下, UnionExpression evaluate()方法对两个操作数求值,在两种情况下检查结果是否为节点集,然后使用排序合并策略形成并集。

如Gamma 等人所述的设计模式。 , evaluate()方法采用Context参数。 Context对象封装了评估表达式所需的所有上下文信息。

这包括:

  • 有关当前节点和当前节点列表的信息(例如,需要评估XPath函数position()last()
  • 访问com.icl.saxon.Bindery对象,该对象保存变量的值
  • 测试名称的等效性时需要访问表达式范围内的XML名称空间列表

XPath解释器还包括优化功能,这些功能扩展了Gamma 等人的基本解释器模式。

例如:

  • 每个表达式类都有一个simplify()方法,以允许重写表达式。 这使得可以执行与上下文无关的优化。 有时这会导致转换为不同的XPath表达式(例如, title[2=2]被重写为title ,而count($x)=0被重写为not($x) )。 表达式重写经常会利用代表特殊情况的内部类。 例如,表达式section[@title]返回当前元素的所有具有title属性的子<section> 。 由于出现子表达式@title的上下文,可以使用专用类重写它,该类测试当前节点上是否存在给定属性并返回布尔值。
  • 每个表达式类都有一个evaluate()方法和一个enumerate()方法 。 这(在表示节点集的表达式的情况下)允许增量检索节点,而不是一次检索所有节点。 这允许以关系数据库系统采用的典型方式进行流水线执行。 例如,在联合表达式上调用enumerate()可以通过合并其两个操作数的枚举来工作。 只要两个操作数都已经按文档顺序分类,就可以避免为中间节点集分配内存。
  • 表达式可以逐渐减少 ,以消除它们之间的依赖关系。 减少表达式的概念在功能语言中得到了广泛的应用,特别适合于优化诸如XPath之类的语言。 每个表达式类都有一个方法getDependencies() ,该方法返回有关该方法所依赖的上下文方面的信息。 这已经使某些优化成为可能。 例如,如果表达式不使用last()函数,则无需进行前瞻处理来确定上下文列表中有多少个元素。 此外,每个表达式都有一个reduceDependencies()方法,该方法返回一个等效表达式,其中消除了指定的依赖关系,而保留了其他依赖项。 当重复使用相同的表达式时,这很有用。 例如,在执行排序之前,将排序键表达式减少以消除对变量的依赖关系(因为对于列表中的每个节点它们都是相同的)而不是对当前节点的依赖关系(对于列表中的每个项目都会有所不同)名单)。

由于没有副作用,因此XSLT语言为处理器提供了极大的自由,使其可以按其选择的任何顺序评估表达式。 撒克逊人的一般政策是标量值(字符串,数字,布尔值)应尽早评估,而节点集值则应尽早评估。 尽早评估标量值只需执行一次即可实现优化。 通过避免不必要地将大列表保留在内存中,延迟对节点集值的评估可以节省内存。 如果发现(通常如此),结点集唯一要做的就是测试它是否为空,或者获取其第一个元素的值,这还可以节省时间。

最后, 输出程序组件( com.icl.saxon.output.Outputter类)用于控制输出过程。 撒克逊人的结果树通常不会在内存中实现-因为它的节点总是按文档顺序写入,因此只要将它们输出到结果树即可对其进行序列化。 实际上,转换不具有单个结果树,而是具有变化的结果树堆栈,因为诸如<xsl:message><xsl:attribute>类的XSL指令有效地将输出重定向到内部目标,而<xsl:variable>构造一个结果树片段,实际上它本身就是一棵单独的树。 这些元素的解释器代码调用输出程序以切换到新的目标,然后再还原到原始的目标。

使用串行器将外部输出写入文件。 从逻辑上讲,这将结果树作为输入并将其转换为平面文件文档。 如您所见,实际上,结果树没有在内存中实现,因此序列化程序一次按文档顺序递归一个树的节点。 使用类似于SAX2的接口( com.icl.saxon.Emitter )呈现此节点流:与SAX2的不同之处在于如何表示名称和名称空间。 根据XSLT建议书中的定义,有单独的XML,HTML和纯文本输出序列化器。 Saxon还允许将树提供给用户编写的代码以进行进一步处理,或作为输入提供给另一个样式表。 这样,您可以通过依次应用多个样式表来实现多阶段转换。

性能

高性能必须是撒克逊人设计的驱动因素,仅次于符合XSLT规范。 这部分是因为它对用户至关重要,而且还因为在有多个可用的免费XSLT处理器的世界中,性能将成为主要的区别特征。

本节讨论影响XSLT处理器性能的一些因素,以及Saxon在每种情况下用来提高速度的策略。

Java语言问题

人们常说Java很慢。 这样做有一定道理,但声明必须经过仔细限定。

许多人认为Java速度很慢,因为Java生成解释的字节码而不是本机代码。 这曾经是真实的,但对于当今的即时编译器已不再适用。 原始代码的执行速度通常几乎与用C之类的编译语言编写的等效代码几乎一样,有时甚至更好。

Java可能存在问题的地方是内存分配。 与C和C ++不同,Java使用垃圾收集器释放不需要的对象来照顾内存本身。 这给程序员带来了极大的便利,但是创建易于占用内存的程序很容易:由于过度使用虚拟内存而使它们崩溃,或者由于分配对象和分配对象的频率而使它们对垃圾回收器造成极大的压力。已发布。

一些编码技术使内存分配问题最小化。 例如,使用StringBuffer对象而不是String ,使用可重用对象池,等等。 诊断工具可以帮助程序员确定何时使用这些技术。 快速获取代码确实需要进行大量调优,但可以说这仍然比使用诸如C ++之类的语言要容易得多,在C ++中,您必须手动管理所有内存分配。

XSLT处理在树结构的实现方面给Java带来了特殊的挑战。 Java在每个对象的大小(最多32个字节,取决于所使用的Java VM)上施加了相当大的繁文red节开销。 这通常会在内存中产生一个树形结构,该树形结构比源XML文件大许多倍。 例如,空元素<a/> (在源文件中的四个字节)可能会扩大到一个Element对象的节点,一个Name对象的名称,一个String由被引用的对象Name的对象,一个空AttributeList节点,一个空的NamespaceList节点,再加上许多64位对象引用,以将这些对象彼此以及树中的父节点,兄弟节点和子节点链接在一起。 幼稚的实现可以轻松地从这四个字节的源中生成200字节的树存储。 鉴于某些用户正在尝试处理原始大小为100MB或更大的XML文档,其后果是可以预见的,并且通常是致命的。

这是撒克逊人选择拥有自己的树形结构的原因之一。 通过消除实现完整DOM接口的要求,我能够从树中消除一些数据。 删除支持更新的要求特别有用。 例如,对于没有属性的元素,Saxon使用不同的类,因为知道如果元素没有属性开始,则以后再也不会获取。 Saxon使用的另一种技术是优化包含单个子文本节点(例如<b>text</b>的元素的常见情况的存储。

如W3C规范中所述,XPath树模型包括用于属性和名称空间的节点。 由于在转换过程中很少访问这些节点,因此Saxon可以按需构造这些节点,而不是让它们永久占用树上的空间。 (这是Gamma 等人的Flyweight设计模式。 )

Saxon的最新版本又走了一步:使用树实现,其中节点根本不由Java对象表示。 相反,树中的所有信息都表示为整数数组。 所有节点都创建为临时(或轻量级)对象,根据需要构造为这些数组的引用,并在不再需要它们时丢弃。 该树实现(包com.icl.saxon.tinytree )占用的内存少得多,并且构建起来更快,但树导航的速度稍慢。 总体而言,它的性能似乎比标准树更好,因此我将其作为默认树提供。

HashtableVector等标准实用程序类也会影响Java程序的性能。 开发人员发现很容易在整个应用程序中自由使用这些方便的类。 但是,要付出代价。 部分原因是类通常比您实际需要做的更多,因此它们比仅为一个目的设计的类施加更多的开销。 它们还旨在处理多线程方面的最坏情况。 如果您知道一个数据结构不会同时被多个线程访问,则可以通过设计自己的对象而不是使用这些现成的类来节省同步成本。 用数组替换Vector通常会带来好处,唯一的缺点是您需要手动处理数组扩展,而Vectors是自我管理的。

位置路径评估

XPath表达式(最具代表性的一种)是位置路径。 位置路径用于选择源树中的节点。 位置路径主要由起点和多个步骤组成。 它类似于UNIX文件名或URL,不同之处在于每个步骤都选择一组节点而不是单个节点。 例如,./ ./chapter/section选择当前节点的所有<chapter>子代中的所有<section>子代。 原点标识了导航树的起点:它可能是当前节点,源树的根节点,另一棵树的根节点,或者是按值使用键定位的一组节点。 位置路径中的每个步骤都从一个节点导航到一组相关的节点。 每个步骤都是根据导航轴定义的(子轴是默认轴):例如,祖先轴选择所有祖先节点,跟随同级轴选择原始节点的所有后续同胞,子轴选择其子项。 除了指定轴外,每个步骤还可以指定所需节点的类型(例如元素,属性或文本节点),所需节点的名称,并确定节点必须满足的条件(例如,其子节点值以B开头。

为位置路径设计执行策略等同于优化关系查询的问题,尽管该理论目前尚不那么先进,并且所使用的大多数技术与准确按照其描述的方式进行导航的简单策略几乎没有什么不同在规范中。 例如,尽管可以在样式表中指定必须构建为支持关联访问的键(就像SQL中的CREATE INDEX语句一样),但Saxon当前仅使用这些索引来支持显式引用它们的查询(通过使用key()函数),绝不优化使用简单谓词的查询。

撒克逊人目前用于位置路径的优化技术包括:

  • 尽可能避免排序。 许多XSLT指令要求按文档顺序处理节点,因此需要付出一定的努力来按文档顺序检索节点,甚至还要付出更多的努力来检测何时检索节点的自然顺序是按文档顺序还是逆文档顺序,因此无需进行排序。 这样的一个示例是表达式//item (定义为/descendent-or-self::node()/item的缩写)可以被/descendent::item替换,前提是它不使用位置谓词。 后一个表达式自然会按文档顺序检索节点,而前一个表达式可能不会。
  • 减少谓词。 有时这可能导致谓词减小为true或false的常量值,从而简化了整个位置路径。 通常,它只是具有删除公共子表达式的作用。 例如,在过滤器表达式$x[count(.|$y)=count($y)] (这是XSLT 1.0中进行集合相交操作的唯一便捷方法),Saxon将仅评估count($y)一旦。
  • 使用位置谓词提前终止。 诸如para[position() <= 3]类的谓词选择当前节点的前三个<para>子代。 不必显式地将此谓词应用于每个<para>元素以查看其是否为真,因为处理可以在第三个节点之后停止。
  • 优化属性引用。 XPath模型对待属性的方式与子元素几乎相同,这大大简化了XPath语言。 但是,由于一个元素最多可以具有一个具有给定名称的属性,因此可以优化对属性的访问。 该优化还考虑了以下事实:除非需要,否则实际上不会在树上实现属性节点。 这意味着,当XPath表达式child::title扫描所有子元素以查找名称为title那些元素时,类似的expression attribute::title (通常缩写为@title )会直接获取相关属性。
  • 位置路径的延迟评估。 在特定上下文中评估位置路径表达式不会返回内存中节点的实际列表,而是会返回另一个表达式(称为“内涵节点集”类com.icl.saxon.expr.NodeSetIntent )。所有上下文相关性已被删除。 仅在实际使用内涵节点集时,才会枚举其成员:并且根据使用方式的不同,可能根本不需要检索它们。 例如,如果在布尔上下文中使用节点集,则唯一需要的处理就是测试它是否为空。 当第三次使用内涵节点集时,它将扩展存储,用内存交换处理时间。 这就像在SQL中实现视图。

样式表编译

我已经描述了Saxon所做的第一件事是如何将样式表“编译”为装饰树,以便随后高效执行。 这提供了一次仅做一次事情的好机会,而不是每次执行相关指令时都做一次。

在样式表编译阶段完成的一些任务如下:

  • 验证。 在编译阶段可以检测到绝大多数用户错误。 这包括一些乍一看似乎是运行时错误的错误。 XPath表达式使用动态类型(在计算表达式或变量之前,不一定知道表达式或变量的类型)。 但是,对于绝大多数实际的XPath表达式,类型在编译时是已知的。 因此,例如,由于XPath表达式$x+2永远不会返回节点集,因此<xsl:for-each select="$x+2">可以立即识别为错误。 在许多情况下,甚至有可能检测到<xsl:for-each select="$x">是一个错误,因为缺少赋值语句意味着通常可以从其声明中推断出变量的类型。
  • 简化表达。 已经讨论了一些使用的技术。 属性值模板是使用表达式的一个重要上下文。 例如,文字结果元素<td width="{$x * 2}">输出一个<td>元素,其width属性是在运行时计算的。 一项重要的编译任务是将属性值模板转换为有效的结构,以便在运行时进行评估。
  • 绑定变量和其他名称。 因为所有变量声明在编译时都是可见的,所以编译器可以提前为每个调用的模板规则在堆栈帧上分配插槽。 然后,可以将表达式中对变量的引用静态绑定到本地堆栈帧或全局变量列表中的特定插槽。 同样,对命名对象(如模板和外部函数)的其他引用通常可以静态解析。 在某些情况下,XPath语法允许动态生成名称(例如,键名或十进制格式的名称),但是仍然有可能检测到以文字形式提供名称然后将其静态绑定的常见情况。

在其他情况下,在编译时执行操作不太可行,但是可以通过避免在运行时重复执行来节省费用。 一个示例是format-number()函数,该函数将描述十进制数字所需的输出格式的模式作为其参数之一。 Considerable savings are possible by detecting the common case where the format pattern is the same as on the previous execution. The only tricky aspect of such optimizations is to keep the memory of previous executions in a place associated with the current thread: it cannot be kept on the style sheet tree itself, as that needs to be thread safe.

Pattern matching for template rules

The pattern matching operation is potentially expensive, so it is vital to focus the search intelligently. The style sheet compiler therefore constructs a decision tree which is used at run time to decide which template rule to apply to a given node.

I'm using the term decision tree here loosely. This section describes the actual data structures and algorithms in a little more detail. (See modules com.icl.saxon.RuleManager and com.icl.saxon.Mode in the source code.)

When the <xsl:apply-templates/> instruction is applied to a node, a template rule must be selected for that node. If there is no matching rule, Saxon uses a built-in rule. If there is more than one rule, the processor resorts to an algorithm from the XSLT specification for deciding which rule takes precedence. This algorithm is based on user-allocated priorities, system-allocated priorities, the precedence relationships established when one style sheet imports another, and -- if all else fails -- the relative order of rules in the source style sheet.

In a typical style sheet, most template rules match element nodes in the tree. Rules to match other nodes, such as text nodes, attributes, and comments, are comparatively rare. Also, most template rules supply the name of the element they must match in full. Rules for unnamed nodes are allowed but not often used (for example, *[string-length(.) > 10] , which matches any element with more than 10 characters of text content).

Saxon's strategy is therefore to separate rules into two kinds: specific rules, where the node type and name are explicitly specified in the pattern, and general rules, where they aren't. The data structure for the decision tree contains a hash table for the specific rules, keyed on the node type and node name, in which each entry is a list of rules sorted by decreasing priority; plus a single list for all the general rules, again in priority order. To find the pattern for a particular node, the processor makes two searches: one for the highest-priority specific rule for the relevant node type and name that matches the node, and one for the highest-priority general rule that matches the node. Whichever of these has highest priority is then chosen.

For a multipart pattern such as chapter/title , the algorithm used is recursive: The match is true if the node being tested matches title and if its parent node matches chapter (module com.icl.saxon.pattern.LocationPathPattern ). This simple approach can't be used for patterns that use positional predicates; for example chapter/para[last()] , which only matches a para element if it is the last one in a chapter. Matching these positional patterns is potentially very expensive, so it's worth handling the common case of a pattern like para[1] specially.

编号

Numbering the nodes on the tree (using the <xsl:number/> instruction) poses a particular optimization challenge. This is because each execution of <xsl:number/> works independently to assign a number to the current node, the number being defined by a complex algorithm using various attributes on the <xsl:number/> instruction. Nothing inherent in the algorithm says that if the last node was numbered 19, the next one will be numbered 20, yet in most common cases that is indeed the case. It is important to detect those common sequential cases. Otherwise the numbering of a large node-set will have O(n2) performance, which is what happens if the numbering algorithm as specified in the XSLT Recommendation is applied to each node independently.

Saxon achieves this optimization for a small number of common cases, where most of the attributes to the numbering algorithm are defaulted. Specifically, it remembers the most recent result of an <xsl:number/> instruction, and if certain rather complex but frequently satisfied conditions are true, it knows that it can number a node by adding one to this remembered number.

Finally

I have tried in this article to give an overview of the internals of the Saxon XSLT processor, and in particular of some of the techniques it uses to improve the speed of transformation. In the 18 months or so since I released the first early versions of Saxon, performance has improved by a factor of 20 (or more, in the case of runs that were thrashing for lack of memory).

It's unlikely that the next 18 months will see a similar improvement. However, there is still plenty of scope, especially for constructs like <xsl:number/> . To take another example, Saxon has not even started to explore to possibilities opened by parallel execution, something the language makes a highly attractive option.

Perhaps the biggest research challenge is to write an XSLT processor that can operate without building the source tree in memory. Many people would welcome such a development, but it certainly isn't an easy thing to do.

后记

迈克尔·凯(Michael Kay)在2005年4月写道:尽管这篇文章是在4年前写的,但它经受了时间的考验。 Underneath the surface, the technology has become a lot more sophisticated (for example, much more optimization is now done at compile time), but the high-level architecture of Saxon is still much as described here.


翻译自: https://www.ibm.com/developerworks/xml/library/x-xslt2/index.html

撒克逊人:XSLT处理器的剖析相关推荐

  1. Saxon: 剖析 XSLT 处理器

    我希望这篇文章能达到几个目的.首先,我希望它可以让样式表作者了解 XSLT 可以实现哪种类型的优化,以及哪些结构当前还没有优化.当然,这种优化的细节在各个处理器以及各个发行版之间都各不相同,但我希望通 ...

  2. 奔腾IV处理器架构剖析

    奔腾IV处理器架构剖析              Intel于美国西部时间20日上午(北京时间20日晚间)发布了Pentium4 1.4GHz 和Pentium4 1.5GHz处理器.我们在Intel ...

  3. 英菲克I5M_I6M_I7M_I10M-晶晨S805处理器-当贝纯净桌面-线刷固件包

    英菲克I5M_I6M_I7M_I10M-晶晨S805处理器-当贝纯净桌面-线刷固件包 特点: 1.适用于对应型号的电视盒子刷机: 2.开放原厂固件屏蔽的市场安装和u盘安装apk: 3.修改dns,三网 ...

  4. 关于亚马逊人的财务自由

    相信财务焦虑是普遍现象,现在分享几位亚马逊人关于财务自由的观点希望对大家有帮助- (一) 首先你要定义财务自由,不同起点,不同家庭出来的人,对财务自由的感受完全不一样 我是18线小城市出来的人,从小家 ...

  5. KS检验、t检验、f检验、Grubbs检验、狄克逊(Dixon)检验、卡方检验小结

    1. KS检验 Kolmogorov-Smirnov检验是基于累计分布函数的,用于检验一个分布是否符合某种理论分布或比较两个经验分布是否有显著差异. 单样本K-S检验是用来检验一个数据的观测经验分布是 ...

  6. 盖师贝格-撒克斯通(GS)算法及其改进算法

    盖师贝格-撒克斯通(GS)算法及其改进算法 本文摘自李景镇<光学手册> 盖师贝格(R.W.Gerchberg)和撒克斯通(W.O.Saxton)首先提出了一种振幅相位恢复算法,即GS算 法 ...

  7. 克克星人的狗屁言论以及等等

    一说起克克星人的麻将桌我首先想到的就是四个外星人,长相参考ALIENWARE公司的注册商标,然后在一起傻蛋一样的大玩麻将.至于那个桌子会不会自动洗牌都是琐事~~~~~~ 然后还可以自然而然地想起点其他 ...

  8. java saxon,如何在Java中将Saxon设置为Xslt处理器?

    小编典典 有多种方法可以执行此操作(按查找优先级顺序): 直接实例化 显式实例化Saxon工厂(对Michael在上面的评论致意): TransformerFactory fact = new net ...

  9. saxon java_如何将Saxon设置为Java中的Xslt处理器?

    有多种方法(按查找优先顺序)执行此操作: 直接实例化 显式实例化Saxon factory(点击Michael上面的注释): TransformerFactory fact = new net.sf. ...

最新文章

  1. 一文彻底搞懂 zookeeper 核心知识点(修订版)
  2. 李飞飞:物体识别之后,计算机视觉的进展、目标和前景何在?
  3. 由PostgreSQL的区域与字符集说起
  4. 【Python学习系列九】Python机器学习库scikit-learn实现SVM案例
  5. 单片机 10种软件滤波方法的示例程序
  6. 动态库、静态库、运行时库、引入库之间的区别
  7. Python列表的生成
  8. Maven pom 文件解释
  9. 应用css div进行页面布局设计,利用CSS与DIV进行页面布局.ppt
  10. topcoder srm 625 div1
  11. mybatis jdbcType 对应javaType
  12. android点击运行后无法显示设备,Android仿真器除了黑屏外什么都不显示,adb设备显示“设备离线”...
  13. 如何面对不讲信用的人
  14. matlab矩阵转置函数
  15. 化繁从简,别让思维打了结
  16. 学好线性代数,我推荐这本书
  17. 开发谷歌浏览器翻译插件
  18. poj2706 connect
  19. CAD自学笔记21条,零基础学CAD可以看看
  20. 在上海相亲碰到如此势利贪婪的“80后”女孩

热门文章

  1. 名帖181 黄庭坚 行书《苦笋赋》
  2. Excel VBA 之 UBound
  3. 通达信破底翻形态选股公式,选出破底之后再翻回的标的
  4. ​LeetCode刷题实战623:在二叉树中增加一行
  5. Cookie的应用---十天免登陆设计
  6. GPU技术支持-基础篇-包管理工具
  7. 中国用于先天性代谢错误的医用食品市场深度研究分析报告
  8. 小米扫地机器人换了边刷很响_米家/石头/小瓦扫扫地机器人为什么不使用双边刷而使用单边刷?...
  9. 经典算法 之 子数组换位问题
  10. 【假设检验】单侧检验时,对拒绝域在左侧还是右侧的理解