(转)借助 VTD-XML* 改进 XML 处理

(原文)http://software.intel.com/zh-cn/articles/31717/

XML 性能问题
作者:Jimmy Zhang
本文介绍了 VTD-XML*,这是一种全新开放源代码的非提取性的(non-extractive) XML 处理 API。本文还深入探讨了 VTD-XML 技术细节,并阐述了 VTD-XML 能够综合 DOM 和 SAX 的优势,独一无二地支持不断更新和“一次解析,多次使用”的原因。

还记得有多少人曾在私下或设计大会上跟您抱怨过 XML 的性能吗?事实上,正是由于 XML 性能不佳以及冗长等缺点,万维网联盟(W3C)于去年做出成立二进制 XML 评定工作组的决定。

XML 被设计成一种互联网的数据交换格式。而冗长的特点是由 XML 的多核心优势所带来的。如:半结构化、开放式互操作、以及可以被人读懂(human-readable)等优势。幸运的是,随着带宽价格不断下降以及容量更加充裕,过去的难题在今天看来可能并不是什么大问题,在未来可能更算不上是问题了。

与冗长相比,XML 的性能则完全是另一回事。试想:XML 不会跑,它既没有腿也没有轮子;XML 也不真正执行,它根本就不是“.exe”文件。实际上,软件开发人员都选择一种处理模式来读取、写入或更新 XML 数据。严格地说来,性能优劣并不是 XML 本身的问题,而是 XML 处理模式的问题。因此,了解一些与当前 XML 处理模式有关的技术问题很有必要,例如:文档对象模型(DOM)和 SAX。
DOM 和 SAX 技术分析
DOM 是一个基于树结构的内存中 XML 处理 API,其设计独立于平台和语言。开发人员可以使用 DOM 创建 XML 文档,浏览其结构,添加、更改或删除其网元。DOM 中一个非常重要的概念就是节点接口模型:DOM 的分级表示中的每个数据对象均可实现节点接口。由于 DOM 加载内存中所有内容,并提供 XML 数据的分级视图,因此开发人员通常会感觉 XML 的使用非常简单方便。其缺点是构建 DOM 树所用时间较长,并且需要占用大量内存(一般大小是 XML 文档的 5 至 10 倍)。

导致 DOM 性能问题的根源在于面向对象的程序设计(OOP)存在薄弱环节。创建 DOM 树涉及大批对象的动态分配,用时下流行的 OOP 语言来说,就是非常昂贵。更糟的是,当这些对象越界时,需要像收集垃圾一样将其收集起来。并且,分配大批对象会导致在合计每对象内存开销时出现严重的内存膨胀(memory bloat)现象。

另一方面,SAX 用于查找 DOM 的内存使用浪费问题。为了达到这一目的,SAX 将一个低级的标记程序(tokenizer)直接导出至调用它的应用程序。SAX 解析器本身并不构建 XML 文档的任何内存中表达。SAX 的内存使用率较低,且不会随着文档大小的增加而扩展,但糟糕的是 SAX 很难使用。SAX 不会在内存中建立树,使用 SAX 的开发人员通常需要将其应用程序调整为事件驱动形式,以适应解析例程。这就会创建冗长且难以维护的代码,从而加重开发人员的负担。

对于需要重复访问 XML 中数据要素的应用程序,SAX 还带来了以下难题:要么对应用程序进行编码以多次扫描 XML 文档,要么创建内存中结构。尽管 SAX 的原始性能很出色,但多次扫描文档会大大降低其相比 DOM 的性能优势。如果有其它选择,您会发现您其实是在构建 DOM。因此,为何不一开始就使用 DOM 呢?

总之,XML 处理模型性能的提高不应以降低可用性为代价。分配大量对象会造成随机访问 API 性能及内存开销。

为什么说了解 DOM 和 SAX 问题非常重要?因为解决问题的方法通常都是寻找多种不同的途径来完成同一个任务。在下文中您将会看到,构成传统 XML 处理技术的许多步骤和设计决定都很简单,而且有时毫无特别之处,而将这些简单的方法综合起来往往能够达到同样的目标并获得更好的结果。

了解 VTD-XML
简介

VTD-XML 是一种基于 Java* 的新型开放源代码 XML 处理 API,能够解决当前 XML 处理模型的许多问题。此方案目前属于 Sourceforge* 一部分,可在此处*找到。通过本演示*,您将熟悉这些基本的概念。仅凭这一点,我们还不能认为 VTD-XML 是专门为此而设计的,因为从第一步——断词(tokenization)开始,它就引入了大量优化技术。

基于偏移和长度的“Non-Extractive”断词(tokenization)。

传统上第一步,XML 分析器将输入的 XML 文档拆分为多个包含相关文本数据的标记(token)。然后,该解析器或将这些标记作为 SAX 事件传送给用户,或都构建基于分级数据结构的内存中对象。断词(tokenization)后,该解析器通常会将输入源文档丢弃。这些标记,即字符串,就是解析过程中分配的字符的空结束阵列。这种断方式即“extractive”断词,解析器实际上是将文本内容从输入文档提取到动态创建的字符串中。

但是,为了实现断词目的,还有一种简单的方法,即仅用开始偏移和长度来描述标记。这种方法也需要将源文件以禁止解码的格式完整保存在内存中。基本上,解析器将 XML 文档视为一个大标记筐,并创建详细描述 XML 中标记位置的地图。此种断词方式称为“non-extractive”,解析器会将标记与原文中保持一样。

阐述“non-extractive”断词方式工作原理的最佳途径就是将其与一般使用场景中的传统“extractive”标记进行比较:
    • 字符串比较: 使用 extractive 断词,开发人员使用 C 语言的"strcmp" 功能(见<string.h />) 来比较“extractive”标记和已知字符串。而使用“non-extractive”断词,开发人员只需使用<string.h>中 C 的“strncmp”功能即可。
    • 字符串到数字数据的转换: 其它经常使用的宏,如 "atoi" 和 "atof" 可以用 non-extractive 标记进行修正,使其正常工作。可以修改功能的签名。例如,"atoi" 将一个字符串作为输入。为达到与 non-extractive 相同的目的,简单创建一个可接受以下三个变量的“新 atoi”:(char* 类型的)源文档、(int 类型的)偏移和(int 类型的)长度。执行中的主要区别在于如何处理新字符串/标记表达(如:字符串的结尾不再标记为 \0)。
  • 裁减: 删除“non-extractive”标记字头及字尾空白空间只需要更改偏移和长度值。这通常比断词(tokenization)的抽取方式更简单,断词通常涉及新字符串的创建。
使用整数,而不是对象

一旦确定基于偏移/长度的断词(tokenization)可以运行,便会产生另一个问题:“标记必须是对象吗?”对象最基本的功能在于将多个成员变量/域绑定在一起。在面向对象的语言中,对象的概念还包括为访问控制和成员方法提供额外支持。然而,根据上文对 DOM 问题的论述,分配大量对象对性能和内存使用开销都会产生负面影响。因此,最好还是绕开对象。

幸运的是,目前存在一种简单的对象替代选择,可使用整数绑定多个变量。事实上,对象和整数的物理表达形式非常相似:它们都是位填充的小内存块。至于绑定部分,可以将所有位分成不同的组,表示不同的变量。例如,一个 32 位寄存器可以绑定四个 8 位整数、两个 16 位整数,甚至 32 个单位(single-bit)布尔值。要注意的是,这种整数的特殊用法在 X86 架构中非常普遍。例如,英特尔® 奔腾® 处理器中的各种网段寄存器和处理器状态寄存器都是 64 位,其中定制了许多子变量。

VTD-XML 基于称为虚拟标记描述符(VTD)的“non-extractive”二进制编码规范,在内部存储和表现 XML 的标记形式。VTD 记录是 64 位整数,对标记的起始偏移、长度、类型和嵌套深度进行 XML 编码。在数位分布层面,VTD 记录定义如下:
    • 起始偏移: 30位(b29 ~ b0)
    • 长度: 20位(b51 ~ b32)
    • 对某些标记类型来说:
        • 前缀长度: 9位(b51~ b43)
      • 合法名长度: 11位(b42 ~ b 32)
    • 嵌套深度: 8位(b59~b52), 最大值为2^8-2 = 254
    • 标记类型: 4位(b63~b60)
    • 保留位: 2位(b31: b30) 作为三态变量名称空间。
  • 单位: 由于处理模型没有对 XML 解码,因此偏移和长度的单位都是转化格式的原始字符。对于 UTF-8 和 ISO-8859,长度和偏移的单位是字节。对于 UTF-16 来说是 16 位字。图 1 描述了 VTD 记录的数位分布情况。

在 DOM 中,一切都是节点(无论网元、文本、CDATA 或属性),内存中表达包括大量由指示器缝合起来的节点对象。把一切当作节点对象的例程会降低性能,同时增加内存使用开销。这是 DOM 处理速度缓慢的一个主要原因。试想:文本节点和属性节点都没有子节点,则把它们列入分级结构不仅会降低性能,而且也没有必要。

为了提供随机存取功能,VTD-XML 采取了使用位置缓存的基于网元的目录方法。位置缓存基本上类似于工作簿的索引部分,不同的是位置缓存是嵌套的目录。位置缓存只将网元(实际上是起始标记的 VTD 索引值)作为分级要素。位置缓存条目是一个 64 位整数,包含两个 32 位域:分别用于全球 VTD 索引和下一级位置缓存的相对索引。VTD-XML 的项目网站上提供了位置缓存的详尽描述以及 VTD 导航器的行为描述。
 
VTD-XML 如何解决 XML 的性能问题
高性能软件

在 Java 中实施时,VTD-XML 通过 NULL 内容处理程序实现 SAX 的性能等级,消耗的内存一般是在一个 XML 文档(大约一个 DOM 树的 1/3 到 1/5)尺寸的 1.3 和 1.5 倍之间。

为什么 VTD-XML 能够同时达到高性能与低内存使用呢?关键是减少了对象的创建。在许多基于虚拟机(VM)的 OOP 语言中,每一对象分配都会导致少量内存开销。VTD 记录不会受到此内存开销的影响,因为它们是整数而不是对象。另外,因为 VTD 记录的长度是不变的,所以它们能够被存到大内存块中,从而更有效地分配和 GC。例如,通过为 4096 VTD 记录分配一个大阵列,会导致每阵列的开销(在 JDK* 1.4 中为 16 字节),这样对每记录开销的减少是非常少的。至于性能,在对象分配与垃圾收集中占用较小内存意味成本更低。实际上,VTD-XML 的高性能是其有效内存使用的一个副产品。

定制硬件实施

随着更多 Web 服务应用通过网络交换 XML 信息,对于进行高速处理、过滤和保护信息的安全来说,网络工具变得越来越重要了。密切相关的主题是在芯片上移植 XML 处理。需要指出的是,VTD-XML 是被设计作为一个可以定制硬件实施的处理模型,能够实现千兆位性能等级。本文章对此没有进行深入的探讨,其基本思想是:VTD 使 XML 处理转变成为与 DES 加密密切相关的问题,DES 加密在定制硬件中实施时能够实现高性能。文章“在芯片上实现 XML”,详细讨论了该主题。

二进制增强型 XML

因为 VTD 和位置缓存基于 64 位整数,解析 XML 的 VTD-XML 的内部表述是持久稳固的,并可保存到磁盘或通过网络发送,以完全消除解析对资源的占用。换句话说,VTD 连同位置缓存与 XML 文档本身一起提供一个简单而有针对性的方式去索引 XML。更重要的是,索引 XML 不会导致 XML 内在优势的损失,比如可读性,以及 VTD-XML 保持 XML 在内存中的完整性。
VTD-XML 的其他优势
不断更新

考虑修改下列 XML 文件的文本内容。

<color> red </color>

使用 DOM,它至少需要以下三个步骤:建立 DOM 树,导航至文本节点并进行更新,然后将已更新的结构写入 XML。因此,无论修改是多么的微不足道,分析并来回编写文档都需要一个往返过程。如果一个大文档仅有一点细小的缺陷将会怎样?如果能像做手术般的去除它,然后将此更新插入到适当位置,那该多好啊!

VTD-XML 的非提取性断词(non-extractive tokenization)的直接优势是允许您正确完成它这操作。因为 VTD-XML 内部可保持 XML 文档的完整性和不被解码,并使用偏移/长度描述标记,因此不用重新串行化该文档的无关部分即可改变内容。例如,当把字节内容写入输出数据流时,通过由相应 VTD 记录指定的“空白”窗口,您可以删除一个标记。继续到下一步,您也可以通过使用该网元第一个和最后一个标记的 VTD 记录计算它的偏差和长度,从而一次清除整个网元。

内容抽取

同样,通过计算开始的偏移和长度,您可以处理 XML 文档的字节阵列中的一个元素,并将其从串行格式中剔除。使用该特性,您能够以前所未有的方式有效处理 XML 信息。例如,假设有两个用 VTD-XML 进行解析的 XML 文件。您可以从第一个文档中逐字地抽取片段,然后将其放到第二个文档的所需位置,它们仅占用很少的 CPU。实际上,VTD-XML 将更改和内容抽取转变成为字节缓冲处理操作。

易用性

意指 DOM 和 SAM 上的初期讨论,作为可建立一个内存中的可导航数据结构的 DOM 解析器,DOM 被认为更易于使用。但是,使用 DOM 的节点界面进行导航通常会引起不必要的混乱,因此开发人员必须确保进行大量节点测试和外在类型铸造。例如,它们并不试图获得子节点或无子节点,如属性节点。与 DOM 相比,因为 VTD-XML 表示 XML 的一个网元级,其导航更加简单有序,通常会使应用程度代码更简短。
结论
总而言之,解决 XML 的性能问题都是为了改进 XML 处理模块。本文介绍了 VTD-XML,它是一个全新的、开放源代码的 non-extractive XML 处理 API,不仅能解决现有处理模式的性能问题,还可提供许多新特性和优势。因为,通过采用优于传统方法的 XML 处理手段,并从第一步开始,VTD-XML 能够很好地实现其设计目的。当 XML 日益成为 IT 基础设施不可或缺的一部分时,VTD-XML 会应用到更多出色的 XML 应用中。
其它参考资料
作者简介
Jimmy Zhang 是 XimpleWare 的创始人和高性能 XML 处理解决方案的提供者。他在电子设计自动化与 IP 语音领域有着丰富的经验,并曾服务于硅谷的多家高科技公司。他毕业于加州大学伯克利分校,获得了电子工程与计算机科学学士和硕士学位。
posted @ 2011-07-31 21:17  JStar  阅读(1265)  评论(0编辑  收藏  举报