在企业基础设施中,以XML编码的业务数据在网络传输时需要5到10倍的带宽,并且存储时也需要更多的磁盘空间。尽管大多数人认为XML的冗余性是其编码信息方式的固有特点(例如,广泛使用标签和尖括号),但关于XML性能问题的解释仍然没有定论。一种普遍的看法是,由于XML是人类可读的文本,它必须是慢速和低效的。同样,二进制XML的支持者似乎暗示,一个紧凑的编码格式,尤其是二进制XML,将自动带来更好的处理性能。
医生在没有诊断的情况下开药是否合理?无论这些看法和信念是否属实,有一点是肯定的:如果没有对XML性能问题的深入理解,将很难,甚至不可能设计出有意义的解决方案。因此,在本文中,将尝试通过关注以下三个关键问题来剖析XML的性能问题:
在网络系统设计中,OSI(开放系统互联)模型是将网络功能划分为七层的标准模型。每一层只使用下一层的功能,并且只向上一层提供功能。与单体方法相比,OSI分层方法的优势在于,面对快速技术演变,网络系统的健壮性、弹性和可扩展性。例如,任何VoIP应用程序都可以在不知道网络的物理层(例如,使用铜缆、光纤电缆或Wi-Fi)或数据链路层(例如,以太网、帧中继或ATM)的情况下工作。
同样,可以采取类似的分层方法来建模基于XML的应用程序。图1是这个“XML协议栈”的简化视图,由三层组成:XML数据层、XML解析层和应用程序层。应用程序层只使用XML解析层导出的功能,该功能将数据从其物理表示(XML)转换为其逻辑表示(信息集)。
关于XML的感知性能问题,可以得出几个观察结果。首先,由于XML应用程序只能像XML解析器处理XML消息一样快,性能实际上不是XML本身的问题,而是XML解析层的问题。如果一个XML路由应用程序(假设应用程序层的开销最小)无法以每秒千兆位的速度跟上传入的XML消息,那么很可能是因为XML解析的吞吐量小于网络的吞吐量。因此,提高应用程序性能的正确方法是优化XML解析层。就像调整任何软件应用程序一样,最好的方法是发现然后找到减少或消除XML解析器开销的方法。
为了了解当前XML解析技术的性能,对两种广泛使用的XML解析器:XercesDOM和Xerces SAX进行了解析吞吐量的基准测试。基准测试应用程序相当简单。它们首先将XML文档读入内存,然后调用解析器例程多次解析文档。通过将文件大小除以每次解析迭代的平均延迟来计算解析吞吐量。对于SAX解析,内容处理程序设置为NULL。选择了几个不同大小、标签性和结构复杂性的XML文档进行基准测试。由一台两岁以上的1.7GHz Pentium M笔记本电脑产生的结果总结在图2中。完整的报告——包括测试设置、方法和代码——可以在线获取。
基准测试结果与DOM和SAX的众所周知的性能特征非常一致。首先,SAX的原始解析吞吐量在20MB/秒到30MB/秒之间,实际上是相当可观的。然而,由于不暴露固有的结构信息,SAX本质上将XML视为带有“尖括号”的CSV,这通常使得它难以使用并且不适合作为通用的XML解析器。另一方面,DOM允许开发人员导航内存中的树结构。然而,基准测试结果也表明,除了非常小的文件外,DOM通常比SAX慢三到五倍。由于DOM解析器通常使用SAX在内部进行标记化,通过比较性能差异,很明显构建内存树结构是瓶颈所在。换句话说,分配所有对象并将它们连接在一起会显著减慢一切。作为下一步,运行了JProfiler(来自ej-Ttechnologies)来确定DOM和SAX解析花费所有CPU周期的地方。结果证实了早期的怀疑,即对象分配的开销在很大程度上限制了DOM解析,并且在较小(但仍然显著)的程度上限制了SAX解析。
一些读者可能会争辩说,DOM——只是一个API规范——并不妨碍高效、较少对象密集型的实现。并非如此。事实上,DOM规范完全基于这样的假设:层次结构完全由暴露节点接口的对象组成。任何DOM实现所能做的最多是改变位于节点接口背后的对象的实现,而不可能完全去掉对象。因此,如果对象创建是主要罪魁祸首,那么DOM规范本身就是使任何面向性能的优化变得非常困难的帮凶。这就是为什么在过去八年中,尽管所有主要IT公司都付出了无数努力,但每个DOM实现的性能改进都只是边缘性的。
二进制XML能从根本上解决XML的性能问题吗?既然性能问题属于XML解析器,那么一个更好的问题是,二进制XML是否可以帮助使解析更有效。答案有两个部分:一个针对SAX,另一个针对DOM。
二进制XML可以提高原始SAX解析速度。二进制XML可以选择不继承许多XML特定的语法特性。例如,二进制XML可以用更有效的东西替换结束标签,完全避免属性,这样SAX解析就不再进行唯一性检查,或者找到其他方法来表示文档结构。有很多方法可以减少SAX标记化成本的CPU周期。二进制XML的支持者经常引用二进制XML版本的SAX比文本XML快10倍的速度提升。
然而,他们忽略了一个简单的事实,即SAX有严重的可用性问题。SAX解析的尴尬的单向性质不仅需要额外的实现工作,而且在文档结构变得稍微复杂时,也会带来性能损失。如果开发人员选择不多次扫描文档,他们将不得不缓冲文档或构建自定义对象模型。此外,SAX与XPath的配合不佳,并且通常不能驱动XSLT处理(二进制XML也需要转换,对吧?)。因此,将SAX的原始性能指向二进制XML作为二进制XML优点的证明既是不公平的,也是误导性的。底线是:对于一个广泛有用的XML处理模型,API必须暴露XML的固有结构。
不幸的是,二进制XML在提高DOM解析方面不会有很大区别。原因很简单,DOM解析通常花费大部分CPU周期来构建内存树结构,而不是标记化。因此,由于更快的SAX解析而带来的DOM加速非常有限。换句话说,二进制XML的DOM解析也会很慢。更进一步,基于对象图的解析器将对几乎所有数据格式(如DCOM、RMI或CORBA)有相同类型的性能问题。XML只是替罪羊。
从基准测试和分析结果来看,很明显,消除XML解析性能瓶颈的最佳方法是减少构建内存层次结构的对象创建成本。可能性实际上是无穷无尽的,并且只受到想象力的限制。好的解决方案可以并且将会涌现,其中之一是VTD-XML。为了实现高性能,VTD-XML通过两个无对象步骤来处理XML解析: