tinyxml很好用,简单,通常情况下足够用,很好。
不过xpath也是很好的东西,tinyxml里面没有直接的支持,网上找了找,有一个tinyxpath,拿下来看看
跑了一个简单的程序,分析一个gccxml生成的xml,文件大概有1m,很莫名其妙的,一个简单的xpath查询,感觉好慢好慢
#include "stdafx.h"
#include "direct.h"
#include <windows.h>
#include <iostream>
#include <string>
#include "xpath_processor.h"
#include "xpath_static.h"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
...{
TiXmlDocument oDoc;
DWORD starttime = GetTickCount();
oDoc.LoadFile("TestGcc.xml");
DWORD endtime = GetTickCount();
cout<<"time use:"<<endtime - starttime<<endl;
if(oDoc.Error())
...{
cout<<oDoc.ErrorDesc()<<endl;
return -1;
}
string str;
cin>>str;
cout<<str<<endl;
starttime = GetTickCount();
TiXmlNode* node = TinyXPath::XNp_xpath_node(oDoc.RootElement(), "//File[@name="TestGccXml.h"]");
endtime = GetTickCount();
cout<<"time use:"<<endtime - starttime<<endl;
cin>>str;
if(node == NULL)
return -1;
TiXmlElement* e = node->ToElement();
if(e == NULL)
return -1;
cout<<e->Value()<<endl;
return 0;
}
就是上面的程序,vc9 release 运行结果:
time use:94
hh
hh
time use:2776
jj
File
请按任意键继续. . .tinyxml分析,大概50~100ms,可以忍受,毕竟这个要求不高
可是xpath的查询,就能到上面的30倍以上,完全忍受不了!
用profile分析,瓶颈存在于node_set::o_exist_in_set上,一共执行了56967次,oh my god!
看了看tinyxpath的代码,结果发现node_set类里面的处理,完全的没有效率

/**//// List of node pointers to the
const void ** vpp_node_set;
/**//// Attributes flag list
bool * op_attrib;
判断是否已有
bool node_set::o_exist_in_set (
const TiXmlBase * XBp_member) /**////< Check if a base exist in the node set
{
unsigned u_node;
for (u_node = 0; u_node < u_nb_node; u_node++)
if (vpp_node_set [u_node] == XBp_member)
return true;
return false;
}
插入操作
vpp_new_list = new const void * [u_nb_node + 1];
op_new_list = new bool [u_nb_node + 1];
if (u_nb_node)
...{
memcpy (vpp_new_list, vpp_node_set, u_nb_node * sizeof (void *));
delete [] vpp_node_set;
memcpy (op_new_list, op_attrib, u_nb_node * sizeof (bool));
delete [] op_attrib;
}
vpp_new_list [u_nb_node] = (const void *) XBp_member;
vpp_node_set = vpp_new_list;
op_new_list [u_nb_node] = o_attrib;
op_attrib = op_new_list;
u_nb_node++;
如上,判断set中是否存在,使用线性查找,添加操作,new出来新数组,然后copy,append
简单,不过,实在慢
自己改了改
加了一个bool值,因为node_set最后要按照xml document顺序排序
unsigned u_nb_node;
/**//// List of node pointers to the
const void ** vpp_node_set;
/**//// Attributes flag list
bool * op_attrib;
bool op_sortbydoc;
构造函数初始化
node_set () 
...{
u_nb_node = 0;
vpp_node_set = NULL;
op_attrib = NULL;
op_sortbydoc = false;
}
判断是否存在的时候,用的是二分查找

/**//// Internal utility function for node set sorting
static int i_compare_ptr (
const void * vp_1, /**////< Ptr to first element to compare
const void * vp_2) ///< Ptr to second element to compare
{
return (*(ptrdiff_t*)vp_1) - (*(ptrdiff_t*)vp_2);
}
static int binary_search_first_not_less_then( void* key, void* arr, int size, int (*func)(const void*, const void*) )
...{
void** k = (void**)key;
void** r = (void**)arr;
int low=0, high=size-1, mid;
while( low < high )
...{
mid = (low + high) / 2;
int cmp_result = func(k, r+mid);
if( cmp_result > 0 )
low = mid + 1;
else if( cmp_result == 0 )
return mid;
else
high = mid;
}
if( low > high )
return -1;
else if(func(k, r+high) > 0)
return -1;
else
return high;
}

/**//// Checks if a node exist in the node set
bool node_set::o_exist_in_set (
const TiXmlBase * XBp_member) /**////< Check if a base exist in the node set
{
int i_index = binary_search_first_not_less_then(&XBp_member, vpp_node_set, u_nb_node, i_compare_ptr);
return i_index >= 0 && vpp_node_set[i_index] == XBp_member;
}
添加的时候,直接排好序,本来要调用o_exist_in_set的,不过为了后面插入方便,直接用binary_search_first_not_less_then函数,找第一个不小于的元素

/**//// Adds a new node in the node set
void node_set::v_add_base_in_set (
const TiXmlBase * XBp_member, /**////< Base to add (node or attribute)
bool o_attrib) ///< true if the base is an attribute, false if it's a node
{
const void ** vpp_new_list;
bool * op_new_list;
if (op_sortbydoc)
...{
op_sortbydoc = false;
for(unsigned i=1; i<u_nb_node; ++i)
...{
int i_insert = binary_search_first_not_less_then(vpp_node_set+i, vpp_node_set, i, i_compare_ptr);
if(i_insert >=0)
...{
const void* vp_tmp = vpp_node_set[i];
bool op_tmp = op_attrib[i];
memmove(vpp_node_set+i_insert+1, vpp_node_set+i_insert, (i-i_insert)*sizeof(void*));
memmove(op_attrib+i_insert+1, op_attrib+i_insert, (i-i_insert)*sizeof(bool));
vpp_node_set[i_insert] = vp_tmp;
op_attrib[i_insert] = op_tmp;
}
}
qsort (vpp_node_set, u_nb_node, sizeof (vpp_node_set[0]), i_compare_ptr);
}
int i_index = binary_search_first_not_less_then(&XBp_member, vpp_node_set, u_nb_node, i_compare_ptr);
if(i_index >= 0 && vpp_node_set[i_index] == XBp_member)
return;
//if (o_exist_in_set (XBp_member))
// return;
vpp_new_list = new const void * [u_nb_node + 1];
op_new_list = new bool [u_nb_node + 1];
if (u_nb_node)
...{
if(i_index >= 0)
...{
memcpy (vpp_new_list, vpp_node_set, i_index * sizeof (void *));
memcpy (vpp_new_list+i_index+1,
vpp_node_set + i_index,
(u_nb_node-i_index) * sizeof (void *));
delete [] vpp_node_set;
memcpy (op_new_list, op_attrib, i_index * sizeof (bool));
memcpy (op_new_list+i_index+1,
op_attrib + i_index,
(u_nb_node-i_index) * sizeof (bool));
delete [] op_attrib;
}
else
...{
memcpy (vpp_new_list, vpp_node_set, u_nb_node * sizeof (void *));
delete [] vpp_node_set;
memcpy (op_new_list, op_attrib, u_nb_node * sizeof (bool));
delete [] op_attrib;
i_index = u_nb_node;
}
}
else
i_index = u_nb_node;
vpp_new_list [i_index] = (const void *) XBp_member;
vpp_node_set = vpp_new_list;
op_new_list [i_index] = o_attrib;
op_attrib = op_new_list;
u_nb_node++;
}
最后,在v_document_sort里面给o_sortbydoc设为true
void node_set::v_document_sort (const TiXmlNode * XNp_root)
...{
ptr_2_and_flag * p2afp_list;
unsigned u_node;
if (u_nb_node < 2)
return;
p2afp_list = new ptr_2_and_flag [u_nb_node];
op_sortbydoc = true;
for (u_node = 0; u_node < u_nb_node; u_node++)
...{
p2afp_list [u_node] . vp_node = vpp_node_set [u_node];
p2afp_list [u_node] . o_flag = op_attrib [u_node];
p2afp_list [u_node] . XNp_root = XNp_root;
}
qsort (p2afp_list, u_nb_node, sizeof (ptr_2_and_flag), i_compare_ptr_2_and_flag);
for (u_node = 0; u_node < u_nb_node; u_node++)
...{
vpp_node_set [u_node] = p2afp_list [u_node] . vp_node;
op_attrib [u_node] = p2afp_list [u_node] . o_flag;
}
delete [] p2afp_list;
}
简单来说,就是按照预先排序,二分查找来优化。
运行结果
time use:94
hh
hh
time use:1216
hh
File
请按任意键继续. . .嗯,时间少了一半多,很好。
继续的优化,可以考虑
数组分配操作,可以用类似vector的方式,一次分配一定内存,不要一次增加一个元素
用rb树代替数组
等等
再继续下去,可能就要考虑xpath解析的方式了,很明显,tinyxpath是先分析"//",生成一个node_set,然后再加入其他条件,逐步筛选出结果来。否则,没法解释为什么会调用那么多次v_add_base_in_set。
不过,实际上可以一次筛选,只把符合的结果放入node_set中,这样就应该会快很多。
但,这样子实在是太麻烦,可能tinyxpath内部算法都要大幅改动了,hehe
另外,tinyxpath和tinyxml都是一路货,考虑的是在没有或者只有很少stl的环境下面也能用,所以,最多,只用了std::string,其他的数据结构都没有用标准库
,实际上,node_set直接用std::set实现,应该就能的到最高效率,也不用那么麻烦了。
所以,还是用stl吧,嘿嘿
说句题外话,据说很少有人能直接一次写好二分查找算法,果然没错,这回二分查找还好说,不过二分查找第一个不小于的元素,还是搞出好几个小bug来。。。唉,为什么不用lower_bound呢
最后,还是说一句吧,这个xpath查询可以优化的,其实<File>节点只在第一级而已,所以"//File..."可以用"File..."代替,查询速度嘛,基本上都在1ms以内,hehe
本文探讨了TinyXML和TinyXPath在处理大型XML文件时的性能问题,并通过修改TinyXPath的Node_Set类实现显著提高XPath查询速度。
5711

被折叠的 条评论
为什么被折叠?



