对tinyxpath的一些简单优化

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

tinyxpath 解析简单 的小工具,输出是一个静态库。可 找到xml文档. TinyXml介绍 TinyXml是一个基于DOM模型的、非验证的轻量级C++解释器 一. XML解析模型: 目前XML的解析主要有两大模型:SAX和DOM。 SAX是基于事件的,其基本工作流程是分析XML文档,当发现了一个新的元素时,产生一个对应事件,并调用相应的用户处理函数。这种方式占用内存少,速度快,但用户程序相应得会比较复杂。 DOM(文档对象模型),则是在分析时,一次性的将整个XML文档进行分析,并在内存中形成对应的树结构,同时,向用户提供一系列的接口来访问和编辑该树结构。这种方式占用内存大,速度往往慢于SAX,但可以给用户提供一个面向对象的访问接口,对用户更为友好。 另据说,一些同时提供了SAX和DOM接口的库,是在底层先实现SAX,再在SAX的基础上实现DOM 对于一个特定的XML文档而言,其正确性分为两个层次。 首先是其格式应该符合XML的基本格式要求,比如第一行要有声明,标签的嵌套层次必须前后一致等等,符合这些要求的文件,就是一个合格的XML文件,称作well-formatted。 其次,一个XML文档因其内容的不同还必须在语义上符合相应的标准,这些标准由相应的DTD文件或者Schema文件来定义,符合了这些定义要求的XML文件,称作valid。 因此,解析器也分为两种,一种是验证的,即会跟据XML文件中的声明,用相应的DTD文件对XML文件进行校验,检查它是否满足DTD文件的要求。另一种是忽略DTD文件,只要基本格式正确,就可以进行解析。 就我所知,验证的解析器通常都是比较重量级的。TinyXml不支持验证,但是体积很小,用在解析格式较为简单的XML文件,比如配置文件时,特别的合适。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值