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 hhtime 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