XML解析到底能多快?

来源:互联网 发布:排序的sql语句 编辑:程序博客网 时间:2024/06/05 05:37

【经验总结】XML解析到底能多快?

----by abllen

近来,因为工作的关系,需要对大XML文件进行解析使用,就专门对解析效率问题作了下研究。 

 

场景:

在我们的场景中,会将大量的业务配置信息存放在DB, 便于前端修改, 后端使用时,会先将DB导出到本地XML格式文件,再各自加载。 这里之所以不直连数据库一是考虑后端高并发时的性能问题,二是数据一般变动不频繁,存放XML也合适。

起初也曾参考一些现有的xml解析库,但由于我们的XML文件都是从DB导出的,对有些情况不很合适,因而考虑手写。

 

为什么会手写:

1  允许xml的key为纯数字,因为要从数据库导出到XML,而数据库的内容往往难以避免有纯数字的出现

2  允许空key,数据库里往往使用空字段表示默认,也比较方便

3  兼容xml格式,更好的出错说明

我们希望使用完全的自左向右扫描分析,如对

<a><as od=sdsd</as></a>

能提示a/as key缺少>更好些, 而不是某一行缺少闭合的>不完整

Tips:曾经对一个800K的上万行xml文件进行排错,包括notepad++/chrome/IE 等浏览器的出错信息让人崩溃,往往只有  某一行缺少;等

4  更好的把握

5  学习,只有亲手写并和其他解析器对比,才能知道真正的瓶颈和问题在哪,希望能挖掘性能极限,看解析究竟能有多快

 

经过一段时间的调教,得到如下结果:

1  俺的满足系统要求的XML解析器核心代码只有50行,纯模板,外围功能还支持增量解析合并,易用

2  解析性能可以对比 strlen ,媲美RapidXML,是一般的TinyXML的数十倍。

 

关于RapidXML

1)      号称比TinyXml快30~60倍,是Boost.PropertyTree的默认xml解析器

2)      对一个约1.5M,utf-8编码包含中/英文,有一定层次深度,大约3.3万行的范例文件解析 TinyXml: 54ms  而 RapidXml: 4ms!

(数据参考 http://hi.baidu.com/5iprog/blog/item/8dde8759dff2558f800a18dc.html

3)      可对比strlen

ps:为什么拿strlen对比,

由于解析一般都是对文件字符进行的判断处理,而strlen最简单,且由于对0值判断的特点,多数系统都采用了特别的优化,可以按系统位长(如32bit就是4字节)来进行比较,效率很高并且是稳定的,可以作为基础标尺。

 

测试及结论:

 

机器环境

系统Win2003Server + MinGW gcc4.6.1

AMD单核机器1.8GHz 内存DDR333 1G

(为了能明显看出差异,特别动用了一台老爷机, 当然不用过于担心现在机器的差异,拜AMD不进取所致和频率墙限制,intel那边现在也是缝缝补补又三年,单核的性能这几年几乎没有多少比例提高)

系统内存带宽:2.7GB/s  AIDA64 内存读取性能约 1200MB/s

(现在一般DDR3 1600的内存带宽 12GB/s, AIDA64 检测单通道读取约5.8GB/s)

 

基准数据

范例文件1.5M数据 重复1000次,memcpy 3sstrlen 2.8s,自己写的按字节mystrlen 12.1s

基本上自测的数据是上述AIDA64测试性能的再对半折,这里面也可以看出X86的字节不对齐影响不大。

 

自测结果

仍旧是范例文件, 重复1000次,RapidXML 14.2s MyParse 14.1s,与按字节写的mystrlen基本相当!!!多出的主要是些逻辑判断等,这应该是目前语言层面所能达到的基本极限了,更深的挖掘,可能需要考虑CPU扩展指令如SSE的使用了,不过这类解析都是一个前后关联的过程,并行化并不容易。

 

如何做到的:

1)      只做一遍扫描分析,CPU的内部运算及带宽相对内存还是快很多的,一个字串多次比较和每次多个值比较会慢些,所以不要做多次子串的扫描,在测试中这减少了近2s

而一般的解析器为了简单等原因,可能会对源串做多次子串操作,譬如当分析了一个合规范的段后,剩余内容子段化, A >  A’ + B(A substr or erase A), 这里面都可能包含大量的内存复制操作,会导致性能的迅速低下,如我优化前的解析器,性能就差50倍还多,快两个量级。

2)      使用查表法换时间,当只做一个自左至右的分析时,不可避免会对字串多次比较,

如<abc id1=xxx’ id2=yyy’ />, key abc每一个字符的判断,便不能为 \n \r \t space  = / >

使用查表法便可以解决这个速度问题,现代的cpu缓存一般每核心都有二级256K, 三级2M,足够缓存表值,见RapidXML 

File: rapidxml.hpp  Version: 1.13  2423行

3)      模板编程可趋于减少运行时间,编译器会优化掉中间的多层转换及调用问题

4)      减少内存的分配,特别是小内存的频繁分配释放

在测试中只将每个key、id、value转换成string,时间就已拉升到了40ms左右,转换到简单DOM树要50ms,RapidXML则使用了自己特别的mempool(RapidXML只从内存解析,使用时会修改给定的内存段并复制),几乎避免了内存的多次分配,时间也就几乎保持了。

 

最后谈一下文本与二进制协议的看法:

1  二进制协议其实也要有拷贝及判断等,极限情况下,只算一次比较及复制(二进制也难以避免多次读取判断, 特别是字段复杂和多的情况下),按字符比较占1/3算,需要

4×1/3 + 1×2/3= 2,即2倍strlen,加上更复杂的逻辑运算,仅比XML解析快一倍不到

2  解析往往不是问题,存储才是。RAPIDXML之所以快,还在于mempool的应用, 基本没有内存的来回拷贝和小对象问题, 而一般的应用,包括我们的解析库,最后都回到了对mapSTL的使用,STL不可避免会有多次的内存拷贝,加上中间小对象的频繁分配释放,性能会立马降低几倍,在这个比较上,纯粹解析的差别就微乎其微。在实际的测试中,存储往往会是一个更大的影响, 而非解析。测试中, 将映射关系存入map,立即就到 ~50ms

3  选择自己喜欢的协议来吧,这真的不是太大问题, 建议不非常渴求的情况下, 使用字符串或xml可读性及可维护性好的协议

4  对于简单又短小的协议,可用二进制

5  一家之言,欢迎PK讨论J

本文原创自无线技术运营空间: http://wireless.qzone.qq.com 及 http://blog.csdn.net/wireless_tech (专注无线技术运营——无线技术(操作系统/数据库/WEB前端/负载均衡/系统容灾/系统安全/短信接入/WAP接入/3G)、无线业务运营、无线开放平台、统计分析(用户行为分析/数据挖掘)CP合作,联系我们:1780551083@qq.com 

原创粉丝点击