级别: 中级 Cameron Laird (Cameron@Lairds.com), 副总裁, Phaseit,Inc.
2003 年 3 月 01 日 面向 XML 的应用程序在性能方面差异巨大。本文是有关 XML 持久性系列文章的第二部分,提供了您应当了解的关于 XML 解析的基本信息,包括几个衡量 XML 解析性能的原则,对于任何希望获得更快速度的 XML 开发人员而言,这些原则都是非常重要的。
在这个系列的有关 XML 性能的文章中,我经常会回顾三个通用的设计原则:
- 功能相同的两个程序的性能经常会有几个数量级的差异。
- 对一项某个组织可以接受的技术进行调优比强行使用一项不熟悉技术的成本通常要低,尽管后者能够提供更快的速度。
- 清晰和完整地说明项目需求的益处很明显。
项目需求应当是
可操作的,或者是客观的。例如,一项陈述“当应用程序在具备 64 MB RAM 的 400 MHz 奔腾机器上运行时,系统必须在 4 秒钟内响应用户查询”的需求,比“我们应该使用快速的解析器”有用的多。
在将这些原则应用于 XML 解析器市场之前,我想解决一些遗留问题。在
本系列文章的第 1 部分,我补充说明了
何时使用 XML 数据库,其中介绍了 XML 持久性。持久性机制和解析协同工作,构成了 XML 编程基础结构最重要的部分。
解析 XML
解析 XML 实例文档很少会成为实际应用程序的瓶颈。对于大多数生产级别的 XML 处理器而言,检查输入流中的单个字符、使其符号化、并且按照 XML 的格式分析它们所花费的时间通常不会限制应用程序。
但是,对相关的文档对象模型(DOM)的构造却很费时。DOM 是树,通常会有许多任意的数据“点缀”于它们的节点上。管理内存中的 DOM 实例会使内存分配方案负担过重,因此 XML 解析器的整体速度对内存分配通常都是很敏感的。
用 Java 编码的解决方案看上去特别易受这一点影响。至少有一打不同的基于 Java 的 XML 处理器可以使用。其中,大多数处理器都没有进行性能调优,以便于有效地使用内存,对于一个 DOM 实例,它们使用的 RAM 可以达到按原始字节计算的输入 XML 流的五倍。大小为几兆字节范围内的 XML 文档的 DOM 树的大小几乎是系统内存的一个或两个数量级;这通常会导致应用程序过度切换正在使用的内存。性能直线下降。
对于 XML 解析器,我通常采用与 XML 数据存储相同的方法 — 设法使用某个组织已经认为合适的技术。有一点是不变的,即有许多方法调优任何现有的系统以提高其响应速度。
解析器比数据存储更缺乏策略性。比起采用一个新的后端数据管理器而言,信息技术(IT)部门可能更乐于为一个应用程序或套件采用一项不同的解析技术。有了这个自由,面对运行得非常缓慢的 XML 应用程序,开发人员应当考虑下面的可能性。
使用 SAX
需要考虑的第一个改进就是使用“用于 XML 的简单 API(Simple API for XML(SAX))”。从原理上讲,这会消除所有与 DOM 构造相关的开销。更确切地说,它是用特定于应用程序的对象模型的开发和构造做了“交易”。如果模型足够简单 — 并且众多的生产应用程序只需要 XML 文档中所有信息的一小部分 — 那么 SAX 将拔得头筹。此外,XSLT、XPath 和 XMLPull 是其它三种解析模型,在特定的情形中,它们比基于 DOM 的应用程序可能更快。
理解这些体系结构的区别对于应用程序开发是至关重要的。许多书籍都重点强调面向对象模型的编程风格。对于各种问题而言,这样会形成一种很清晰的设计。但是,DOM 和与它相关的东西要求的内存很多,几个最主要的用 Java 编码的 DOM 解析器在内存开销方面特别昂贵,其总的运行时占用高达 XML 文档实例大小的 5 倍。从另一方面来说,面向事件的 SAX 编程是许多程序员所不熟悉的。当然,如果开发人员认为它非常难以有效地使用的话,那么其潜在的高性能和节约内存的特性就毫无价值了。
expat 是快速的
大多数最快的解析器都是基于 James Clark 的成果。用他网站上的话说,他的
xp “旨在成为用 Java 编写的最快的一致的 XML 解析器”。而较快的是
expat 。许多语言(包括 Perl、Python、Scheme 及至少 20 种其它语言)都可以与这个用 C 编码的库绑定。还有几个特殊用途的面向 DOM 的解析器,那些渴望速度的开发人员应当试验一下。
rxp 可能是最值得验证的,目前仅记录命令行界面。象本文提到的其它几种开放源码解析器一样,
rxp 提供了几个生成选项。例如,不利用 Unicode 能力编译它,可以获得重大的性能改进。把
rxp 应用到重要的开发工作中的一个便捷方法就是通过将
rxp 的 ReportLab 的 pyRXP 绑定到 Python 编程语言中。
特殊用途的具有 DOM 能力的解析器
对于通用开发更令人感兴趣的是 tDOM,可以在
expat 或者独立顾问 D.
Richard Hipp 的
simple XML 解析器之上构造它。尽管后者不支持所有 DOM 2(例如,它不支持外部实体)但是,对于作为简单对象访问协议(SOAP)消息使用而言,它已经足够了。此外,tDOM 在其 DOM 构造方面的设计是特别敏捷和节约内存的。
另一种可供选择的技术是 JDOM,它是一个面向 Java 的性能良好的对象模型,既不是 DOM,也不是 SAX。一个 JDOM 应用程序将使现有的面向 SAX 的 Java 解析器更具编程性和可维护性,而不必转换成完全的 DOM,从而避免了性能上的问题。
去年,
libxml2 以
expat 重要的竞争对手和补充的面目出现。
libxml2 是 GNOME 项目的一部分,象
expat 一样,具有很大的灵活性,是用出色的可移植的 C 编写的,并能够获得卓越的性能。在我的测试中,二者的运行速度大致相同。而且二者的运行速度几乎比其它所有商业 XML 解析引擎都要快的多。
基准测试原则
开发人员 Pankaj Kumar 已经开发了一个面向 Java 的基准测试和性能概括工具 — XPB4J。在评估您面临的选择时,它们是很有用的,并且也是一个有价值的起点。
我的评测趋向于大得多的 XML 实例,而不是 Kumar 先生选择的几十个千字节的实例。根据我的经验,当文档达到这样的大小时,I/O(特别是网络吞吐量)比解析负载更会成为约束。更确切地说,对于这类应用程序,在足够快的解析器中通常都能快速地进行交换。仍然有一些极端情形,其中解析性能确实约束了整体应用程序响应速度。出现这种问题时,代理模式非常适合于解决性能问题:聪明的应用程序会在内存中保存足够多的数据,以此改进平均周转时间,以达到可接受的级别。
我还把 Kumar 先生的和其他专注于其它语言(除了 Java 技术)的 XML 基准测试程序进行了区分。目前用 Java 编码的解析器中,特别是对于大的文档,
expat 的优势是显而易见的。此外,它的灵活性使其切实可行,必要的时候可以实现延迟编程(lazy programming)技术。这也是 SAX 或 XMLPull 接口的优点之一:不必等整个文档解析完成,部分求值结果早就可以开始反馈给用户了。用户喜欢这样的响应印象。出于这些原因,我经常使用
expat 或者上面描述的某个其它专业解析器。
我自己的定量结果的范围趋向于狭窄。XML 解析的基准测试对许多重要的因素都很敏感,通常最重要的一些因素是:
- 考虑使用的 API
- 测试数据的规模
- 主机的处理器速度、I/O 子系统和主内存
实现的语言与性能的关系不很密切;对于大多数常用的语言,既有快速的也有慢速的 XML 解析器。
结束语
弄清您的需求。将它们清晰地表达出来。
当特定项目的性能滞后于其实际需要时,下面是一些花费最小但却可能带来显著改善的策略:
- 更快的硬件
- 可编程的高速缓存或预先读取
- 一个非常适合于特定应用程序的 API(如,DOM 或者 SAX),从而保持可维护性,并且需要更少的处理资源
- 更快的解析引擎
通常,至少由于两个原因使基于
expat 的解析器比其它优秀的基于 Java 的解析器要快。出于同样的原因,专业 XML 解析器胜过
expat 。用相同语言编写的不同解析器可能在速度方面会有足足两个数量级的差异。
在分析解析性能(特别是对于 DOM)时,注意内存的使用情况。如同原始性能一样,在不同的解析器和应用程序算法之间,也会有很大的差异。
本系列有关 XML 性能的下一部分将系统地单步执行一个实际练习,该练习用来测量和提高演示应用程序的速度。
参考资料
关于作者
对本文的评价
|