tinyxml很好用,简单,通常情况下足够用,很好。
不过xpath也是很好的东西,tinyxml里面没有直接的支持,网上找了找,有一个tinyxpath,拿下来看看
跑了一个简单的程序,分析一个gccxml生成的xml,文件大概有1m,很莫名其妙的,一个简单的xpath查询,感觉好慢好慢
#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 运行结果:
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类里面的处理,完全的没有效率
const void ** vpp_node_set;
/**/ /// Attributes flag list
bool * op_attrib;
判断是否已有
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 ;
}
插入操作
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顺序排序
/**/ /// List of node pointers to the
const void ** vpp_node_set;
/**/ /// Attributes flag list
bool * op_attrib;
bool op_sortbydoc;
构造函数初始化
... {
u_nb_node = 0;
vpp_node_set = NULL;
op_attrib = NULL;
op_sortbydoc = false;
}
判断是否存在的时候,用的是二分查找
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函数,找第一个不小于的元素
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
... {
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;
}
简单来说,就是按照预先排序,二分查找来优化。
运行结果
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