新兴XML处理方法VTD-XML介绍

序言

本文所提及的VTD-XML并非本文作者原创,作者只是对它进行介绍。

问题

通常当我们提起XML的使用时,最头痛的部分便是XML的verbosity与XML的解析速度,当需要处理大XML文件时这个问题便变得格外严重。我在这里提及的,便是如何优化XML处理速度的话题。

当我们选择处理XML文件的时候,我们大致上有两种选择:

  1. DOM,这是W3C的标准模型,它将XML的结构信息以树形的方式构建,提供了遍历这颗树的接口与方法。
  2. SAX,一种低级的parser,逐元素的向前只读处理,不含有结构信息。

以上两种选择都各有利弊,但是都不是特别好的解决方案,它们的优缺点如下:

DOM

  1. 优点:易用性强,因为所有的XML结构信息都存在于内存中,并且遍历简单,支持XPath。
  2. 缺点:Parsing速度太慢,内存占用过高(原文件的5x~10x),对于大文件来说几乎不可能使用。

SAX

  1. 优点:Parsing速度快,内存占用不与XML的大小相联系(可以做到XML涨内存不涨)。
  2. 缺点:易用性差,因为没有结构信息,并且无法遍历,不支持XPath。如果需要结构的话只能读一点构造一点,这样的可维护性特别的差。

我们可以看出,基本上DOM与SAX是正好相反的两个极端,但是任何一个都不能很好的满足我们的大部分要求,我们需要找出另外一种处理方法来。注意XML的效率问题并不是XML本身的问题,而是处理XML的Parser的问题,就像我们在上面看到的两种方法有不同的效率权衡一样。

思考

我们很喜欢类似DOM的使用方法,因为我们可以遍历,这意味着可以支持XPath,大大增强了易用性,但是DOM的效率很低。就像我们已经知道,效率问题出在处理机制上。那么,DOM到底有哪些方面影响了它的效率呢?下面让我们来做一个全面的解剖:

  1. 在当今大多数基于虚拟机(托管,或任何类似机制)技术的平台下,对象的创建销毁是一个耗时的作业(这里值得主要是Garbage Collection的耗时),DOM机制中所运用的大量的对象创建销毁无疑是影响其效率的原因之一(会引发过多的Garbage Collection)。
  2. 每个对象都会额外有32bits用来存储它的内存地址,当像DOM一样拥有大量对象的时候这个额外开支也是不小的。
  3. 引起以上两个问题的最主要的效率问题在于,DOM与SAX都是extractive parsing模式,这种解析模式注定了DOM与SAX都需要大量的创建(销毁)对象,引起效率问题。所谓的extractive parsing就是说在解析XML时,DOM或SAX会提取一部分原文件(一般来说是一个字符串),然后在内存中进行解析构建(输出自然就是一个或一些对象了)。拿DOM这个例子来说,DOM会将每一个element, attribute, processing-instruction, comment等等都解析成对象并给与结构,这就是所谓的extractive parsing。
  4. 由extractive的问题带来的另一个问题便是更新效率,在DOM中(SAX因为不支持更新所以根本不提它),每一次需要做改动时,我们要做的就是将对象的信息再解析回XML的字符串,注意这个解析是个完整的解析,也就是说,原文件并没有被利用,而是直接将DOM模型重新完整解析成XML字符串。换句话讲,也就是DOM并不支持Incremental Update(增量更新)。
  5. 另一个很可能不被注意到的“小”问题便是XML的编码,无论是何种解析方法都需要能够处理XML的编码,也就是说,在读取的时候解码,在写入的时候编码。DOM的另一个效率问题便是当我对于一个大XML只想做很小的一块儿修改的时候它也必须首先将整个文件进行解码,然后构建结构。无形中又是一个开销。

让我们来总结一下问题,简单的讲DOM的效率问题主要出在它的extractive parsing模式上(SAX也是一样,有同样的问题),由此引发了一系列相关问题,如果可以击破这些效率瓶颈的话那么可以想象XML的处理效率将进一步的得到提高。如果XML的易用性与处理效率得到飞跃性的提高的话,那么XML的应用范围,应用模式将得到更一步的升华,或许由此可以产生出许许多多精彩的以前连想都没有想过的基于XML的产品来。

出路

VTD-XML便是对以上问题的思考后给出的答案,它是一个non-extractive XML parser,由于它出色的机制,很好的解决(避免)了上面所提出的各种问题,并且还“顺便”带来了non-extractive的其他好处,像快速的解析与遍历、XPath的支持、Incremental Update等等。我这里有一组数据,取自于VTD-XML的官方网站:

  1. VTD-XML的解析速度是SAX(with NULL content handler)的1.5x~2.0x。With NULL content handler的意思就是说SAX解析中没有插入任何额外的处理逻辑,也就是SAX的最高速度。
  2. VTD-XML的内存占用是原XML的1.3x~1.5x(其中1.0x的部分是原XML,0.3x~0.5x是VTD-XML占用的部分),而DOM的内存占用则是原XML的5x~10x。举一个例子,如果一个XML的大小是50MB,那么用VTD-XML读取进来内存占用会在 65MB~75MB之间,而DOM的内存占用则会在250M~500MB之间。基于这个数据用DOM处理大的XML文件几乎是不可能的选择。< /li>

你可能会觉得不可思议,真的可以做出比DOM易用性还好,比SAX还快的XML解析器吗?别急着下定论,还是来看看VTD-XML的原理吧!

基本原理

就像大多数好的产品一样,VTD-XML的原理并不复杂,而是很巧妙。为了实现non-extractive这个目的,它将原XML文件原封不动的以二进制的方式读进内存,连解码都不做,然后在这个byte数组上解析每个element的位置并把一些信息记录下来,之后的遍历操作便在这些保存下来的record上进行,如果需要提取XML内容就利用record中的位置等信息在原始byte数组上进行解码并返回字符串。这一切看起来都很简单,但是,这个简单的过程确有多个性能细节在里边,并且隐藏了若干个潜在的能力。下面我们首先来描述一下各个性能细节:

  1. 为了避免过多的对象创建,VTD-XML决定采用原始的数值类型作为record的类型,这样就可以不必用heap。VTD-XML 的record机制就叫做VTD(Virtual Token Descriptor),VTD将性能瓶颈在tokenization阶段就解决掉了真的是很巧妙很用心的做法。VTD是一个64bits长度的数值类型,记录了每个element的起始位置(offset),长度(length),深度(depth)以及token的类型(type)等信息。
  2. 注意VTD是固定长度的(官方决定用64bits),这样做的目的就是为了提高性能,因为长度固定,在读取,查询等操作的时候格外的高效(O(1)),也就是可以用数组这种高效的结构来组织VTD大大减少了因为大量使用对象而产生的性能问题。
  3. VTD的超能力(一点都不夸张地说)就在于它能够将XML这种树形的数据结构简单的变换成对一个byte数组的操作,任何你能想象到的对于byte数组的操作都可以应用在XML上了。这是因为读取进来的XML是二进制的(byte数组),而VTD则记录了每个element的位置等访问用信息,当我们找到要操作的VTD的时候,只要用offset与length等信息就可以对原始byte数组进行任何操作,或者可以直接对VTD进行操作。举例来说,我想在一个大XML中找出一个element并删除它,那么我只需要找到这个element的VTD(遍历方法稍候再讲),将这个VTD从 VTD数组中删除,然后再利用所有的VTD写出到另一个byte数组中就可以了,因为删除的VTD标明了要删除的element的位置,所以在新写入的 byte数组中就不会出现这段element了,用VTD 写入新的byte数组实际上就是一个byte数组的拷贝,其效率相当的高,这就是所谓的增量更新(incremental update)。

关于VTD-XML的遍历方式,它采用了LC (Location Cache),简单地说就是将VTD以其深度作为标准构建的一个树形的表结构。LC的entry也是64bits长的数值类型,前32bits代表一个 VTD的索引(index),后32bits代表了这个VTD的第一个child的索引。利用这些信息就可以计算出任何一个你想要到达的位置了,关于具体的遍历方法请参看官方网站的文章。基于这种遍历方式的VTD-XML有与DOM不同的操作接口,这是可以理解的,并且,VTD-XML的这种遍历方式可以在最少的几步内将你带到你所需要的地方去,遍历的性能十分突出。

总结

就像你上面看到的,VTD-XML有着迷人的特性,而如今的1.5版本中已经加入了XPath的支持(只要可以遍历,就可以支持 XPath,这是早晚的事:-)),它的实用性已经超越了当今我们所想象的范围了。另一个VTD-XML的超能力,就是基于它现在的处理方式,完全可以支持将来的Binary XML标准,并通过Binary化将XML的应用推向更高一层楼!这也是我目前所期待的!:-)

不过,VTD-XML仍然有许多需要改进与完善的地方,这方面值得我们努力与探讨。

顺便提一下,VTD-XML是开源项目(GPL),目前有Java、C两种平台支持。如果你想在.NET试一试的话建议你使用IKVM(BSD style license)将VTD-XML转换成.NET程序集,相信你会喜欢上它的!;-)

posted @ 2006-03-15 11:49 Cavingdeep 阅读(4069) 评论(26)  编辑 收藏 所属分类: XML

  回复  引用  查看    
#1楼 2006-03-15 12:24 | dudu      
建议用摘要方式发布。
  回复  引用    
#2楼 2006-03-15 12:24 | firelair [未注册用户]
有没有转换好的.net版本?想试试

  回复  引用  查看    
#3楼 2006-03-15 12:36 | Teddy's Knowledge Base      
听起来可真不错~~
  回复  引用  查看    
#4楼 2006-03-15 13:35 | idior      
我知道RDF查询某个元素时有一个特点就是可以兼具sax和dom的优点, 但是不知道具体的实现方法。
  回复  引用  查看    
#5楼 [楼主]2006-03-15 13:51 | Cavingdeep      
@firelair
我自己也是想试试,所以就转换了一个,如果你真的想试用的话还是建议DIY,这样可以从中学到更多的东西,如果你有步骤不清楚的话我可以帮你解答。

@Teddy's Knowledge Base
不只是听起来不错,我用了一下也很不错。:-)尤其我需要处理大量的大XML文件。

@idior
RDF是什么(Resource Description Framework?),有没有链接可以看看的?:-) 多多学习总是好事!
  回复  引用  查看    
#6楼 2006-03-15 14:33 | idior      
When processing XML, an element isn't actually complete until you reach its end tag. If an application is parsing an XML document into elements in memory before transferring them into another persisted form of data, this means that the elements that contain other elements must be retained in memory until their internal data members are processed. This can result in some fairly significant strain on memory use, particularly with larger XML documents.

RDF/XML, on the other hand, would allow you to process the first element quickly because its "contained" data is actually stored in another element somewhere else in the document. As long as the relationship between the two elements can be established through the URI, we'll always be able to reconstruct the original data regardless of how it's been transformed.

Another advantage to the RDF/XML approach is when querying the data. Again, in XML, if you're looking for a specific piece of data, you basically have to provide the entire structure of all the elements preceding the piece of data in order to ensure you have the proper value. As you'll see in RDF/XML, all you have to do is remember the triple nature of the specification, and look for a triple with a pattern matching a specific resource URI, such as a property URI, and you'll find the specific value.

摘自 Oreilly.Practical.RDF
  回复  引用  查看    
#7楼 [楼主]2006-03-15 15:39 | Cavingdeep      
@idior
OK,现在我知道了你说的就是Resource Description Framework,RDF是W3C的又一个Recommendation(2004年),它的作用就是提供资源描述的框架。就像XSD等一样,也是基于XML语言的一种描述语言。

它本身与XML解析器没有任何关系,它也不是一种解析XML的方法。你所quote的文章中的内容是说RDF的组织资源信息的方式便于查询(triple - subject predicate object),并且由于它的信息组织方式,在解析它的过程中也不会读取到特别深的元素(element),这样也就避免了在内存中保留过多的临时信息。(顺便说一下,文章中所说的在解析时因为元素深度所产生的临时信息这一点对VTD-XML的解释方式是不适用的)

现在可以肯定了,据我了解VTD-XML是当前最高效的XML解析方式!:-)
  回复  引用  查看    
#8楼 2006-03-15 15:55 | 韦恩卑鄙      
完全可以支持将来的Binary XML标准,并通过Binary化将XML的应用推向更高一层楼!
不明白了 xml为什么要Binary化
  回复  引用  查看    
#9楼 [楼主]2006-03-15 17:00 | Cavingdeep      
@韦恩卑鄙
XML Binary化有很多的实际用途,W3C有个小组专门负责这块。实际的Use Cases你可以在下面链接中找到:

http://www.w3.org/TR/xbc-use-cases/

简单地说就是因为XML现在还有很多不太适用的领域,不太适用的原因有很多,举其中几个例子来说:

1) XML的verbosity,XML拥有大量的元数据,这是它的设计目标之一,大量的元数据可以简化数据与结构的操作,但是目前基于文字的大量的元数据(字符)使得XML在大批量数据的情况下对于传输、处理等都不是特别理想。

2) 二进制的原始数据类型,XML所携带的数据类型在其本质上统统都是字符类型的,也就是说,我们要在字符串与本地数据类型之间做转换后方可使用,这在要求数学精度、效率、完整性等方面的应用程序上都不能很好的满足需求。

所以说一旦XML Binary Characterization正式出台后,相关的XML工具、框架、各种各样的XML应用将层出不穷,XML的应用将更上一层楼。而今天,VTD-XML已经给我们展示了一种对未来的"Binary XML"的parsing的一种可行性方案。:-)
  回复  引用    
#10楼 2006-03-15 17:35 | idior, [未注册用户]
没错 ,我说的不是xml的解析方式,而是指出一种新的资源描述方式rdf。
你现在需要高效的解析xml,但是当你需要这个需求的时候,有时候可以考虑使用rdf来描述你的信息(而不是基本的xml).
你说的这个东西,我也是第一次听说,应该有很大的价值。我只是指出另一条思路。
  回复  引用  查看    
#11楼 2006-03-15 21:07 | THIN      
sounds good
  回复  引用  查看    
#12楼 2006-03-16 09:49 | myrat      
@韦恩卑鄙
我也没看懂,xml不就是可读性好么?binary了还有什么可读性
  回复  引用  查看    
#13楼 [楼主]2006-03-16 10:28 | Cavingdeep      
@myrat
可读性是针对不同的消费者的,XML的可读性可以是针对人(Human)的,也可以是针对电脑(Computer)的。比如RDF本身就不是给人看的,它就是给程序“看”的。同理,Binary化的XML也不是给人看的(虽然不排除这个可能性),它会是专门为程序间的高效交互设计的。
  回复  引用  查看    
#14楼 2006-03-16 11:23 | 装配脑袋      
XML选取文本格式的一个原因是为了跨平台。
我觉得VTD-XML思路是好的,但是以byte数组作为处理的主体是否合适?他是否支持不同字符集?是否支持namespace的解析?

等我看看他怎么做的,将来说不定可以用VBF写一个Text-based的VTD Parser
  回复  引用  查看    
#15楼 [楼主]2006-03-16 11:46 | Cavingdeep      
@装配脑袋
关于字符集与命名空间的问题我现在就可以回答你,VTD-XML 1.5支持namespace(你可以看到相关的调用接口,实际上就是属性与前缀,统一用attribute与prefix就支持了),而且你可以使用不同的字符集,这就是我在文章中所指的编码(encoding)。

关于使用byte数组的原因,这就是出于效率而考虑的,对字符串的操作永远都不能达到对原始数据操作的效率与通用性(这也是XML Binary Characterization的motivation之一)。另外使用byte数组这种原始数据还有很多额外的好处,在文章中也有提到过(比如编码解码的开销、数据类型转换的开销、Incremental Update等)。
  回复  引用  查看    
#16楼 2006-03-16 15:00 | ttyp      
有部分没看明白,如果做删除操作时像删除一个数组得元素一样,但是如果这个元素有子对象时,它怎么做得呢?有对应得关系表么?
  回复  引用  查看    
#17楼 [楼主]2006-03-16 17:05 | Cavingdeep      
@ttyp
这里的删除不是在原始数据上做的操作,原始的XML是一直保持不变的,这里的删除是对VTD的删除,理论上是完全可以从LC中移除某个VTD的,但是一旦这个VTD被删除了,那么你将再也找不到它和它子元素的VTD了,也就是说,你将无法再对这个VTD所标记的原始XML做任何动作了。所以VTD-XML并没有提供从LC中移除VTD的接口。但是你可以通过获取offset, length来忽略这个VTD所代表的element。如果你想删除一个element的child,那么首先在LC中找到这个child的VTD,然后将它移走(标记为要删除)举个例子:

<root>
<person id="1">
<name>XYZ</name>
<gender>100</gender>
</person>
</root>

如果你想输出的XML中没有<person>段,那么你可以这样做(假设你已经有了原始的xmlArray, VTDNav类型的nav变量和一个输出用的writer):

nav.toElement(VTDNav.ROOT);
nav.toElement(VTDNav.FIRST_CHILD, "person");
long frag = nav.getElementFragment();
int offset = (int) frag;
int length = (int) (frag >> 32);
writer.Write(xmlArray, 0, offset);
writer.Write(xmlArray, offset+length, xmlArray.Length-(offset+length));

这样你的输出就是这样一段了:
<root>

</root>

注意VTD-XML并不需要格式化输出,因为是直截了当的byte数组操作,这就是所谓的incremental update:可以做到不毁坏其他原始数据,只对你感兴趣的地方做修改,这在DOM中是不可能的。

BTW,上面的定位工作当然也可以使用XPath来完成,详见其API文档。
  回复  引用  查看    
#18楼 2006-03-16 17:42 | 装配脑袋      
有必要那么快吗?我想即使基于字符串,也可以比DOM快吧?而且也节省内存。他可能追求极限了,我向各方面权衡一下也会不错的说。
我还是不太放心byte数组可以解决编码和namespace问题,因为后者需要生成name table,总要在parse的时候有个编码才好搞吧……

不管怎么说,先试试。
  回复  引用    
#19楼 2006-06-06 22:22 | 思考 [未注册用户]
看上去很不错,不过不知道为什么没有封装Insert,Delete,Update这些基本操作,请教下自己封装这些操作该如何实现,谢谢
  回复  引用  查看    
#20楼 [楼主]2006-06-09 08:37 | Cavingdeep      
@思考
无疑只能使用VTD所提供的API,具体的做法要看每个人的设计喽,今后如果有充足的时间的话我想我会将VTD封装一下的,然后以GPL发放出来。:)
  回复  引用    
#21楼 2006-07-30 06:56 | VTD-XML owner [未注册用户]
Delete, Insert and Update are features that need to be carefully designed. VTD-XML is not DOM, and does not pretend to be...
I think bit-level manipulation at the byte level is a very useful skill that you will need to have in order to take full advantage of VTD-XML...

  回复  引用    
#22楼 2006-08-18 11:00 | GK [未注册用户]
目前支持XPATH和namespace,可惜对编码的支持不够高,目前支持UTF-8 iso-8859 ,想改一下,但是觉得很麻烦

  回复  引用  查看    
#23楼 [楼主]2006-08-19 08:29 | Cavingdeep      
对XPath的支持不是太好,使用起来也比较蹩脚。:(
  回复  引用    
#24楼 2006-08-20 07:34 | vtd-xml owner [未注册用户]
any suggestions on XPath ??? Join our discusion and help us
improve VTD-XML

https://lists.sourceforge.net/lists/listinfo/vtd-xml-users


  回复  引用    
#25楼 2006-08-20 07:35 | vtd-xml owner [未注册用户]
@GK
what encoding do you have in mind, let us know and we can add it...
  回复  引用    
#26楼 2007-08-08 14:56 | zhang qian [未注册用户]
你好!

将VTD-XML用硬件实现你觉得有意义吗还?thanks!

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-03-30 12:43 编辑过


相关链接: