菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t

 

菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t

 

  • Author:Echo Chen(陈斌)

  • Email:[email protected]

  • Blog:Blog.csdn.net/chen19870707

  • Date:October 28h, 2014

     

    1.什么是基数树

    基数树(radix tree)是一种不怎么常见的数据结构,这里简单的做一下介绍:在计算机科学中,基数树,是一种基于trie(字典树)的特殊的数据结构,可以快速定位叶子结点。radix tree是一种多叉搜索树,每个结点有固定的孩子数(叉数 为2^n)。

    如下图radix树的分叉为4,树的高度为4,共有4*4*4*4 = 256 个叶子结点,可以快速定位256个结点。

    2.ngx_radix_tree_t

    ngx_radix_tree 是一种二叉查找树,即叉数为2,它要求存储的每个节点必须以32位整型作为任意两节点的唯一标识,ngx_radix_tree 具备二叉查找树所有优点,并且不用像红黑树通过自身旋转达到平衡,基数树不用管树的形态是否平衡。也因此,它在插入节点、删除节点的速度会比红黑树快的多。

     

    基数树可以不管树平衡的原因在于:红黑树是通过不同节点key关键字的比较决定树的形态,而基数树的每个节点的key关键字自身已经决定了其在树中的位置。先将节点的key关键字转化为二进制,32位,从左至右开始,遇0入左子树,遇1入右子树。

     

    ngx_radix_tree_t树的最大深度为32,由于一般用不到这样的深度,所以引入了掩码,掩码中的1的个数就表示树的高度,掩码1110 0000 0000 0000 0000 0000 0000 0000 ,表示树的高度为3。

     

    eg:如果此时一个节点的key关键字为0x20000000,根据掩码决定取其转化为二进制后的前3位为010,因此,该节点的位置是,根节点-->左子树-->右子树-->左子树。用下图至关表示下:


    image

    3.源代码位置

     

    头文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_radix_tree.h

    源文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_radix_tree.c

     

    4.数据结构定义

     

    结点中left和right分别指向左右孩子,parent指向父亲结点,value为指向用户自定义的数据的指针。

       1: typedef struct ngx_radix_node_s  ngx_radix_node_t;
       2:  
       3: struct ngx_radix_node_s {
         
       4:     ngx_radix_node_t  *right;
       5:     ngx_radix_node_t  *left;
       6:     ngx_radix_node_t  *parent;
       7:     uintptr_t          value;
       8: };

     

    与红黑树不同的是,radix_tree自己管理内存,pool为内存池对象,root为根节点,free管理已经分配但暂未使用的节点,free实际上是所有不在树中结点的单链表。start为已分配内存中未使用内存的首地址,size为已分配内存还未使用内存的大小。

     

       1: typedef struct {
         
       2:     ngx_radix_node_t  *root;
       3:     ngx_pool_t        *pool;
       4:     ngx_radix_node_t  *free;
       5:     char              *start;
       6:     size_t             size;
       7: } ngx_radix_tree_t;

    5.基数树的创建ngx_radix_tree_create

     

    基数树的构造流程为首先创建 基数树结构 ngx_radix_tree_t ,然后创建 基数树的 root结点,然后根据传入的preallacate参数来决定预分配结点的个数,如果传入-1 ,即按照页面大小决定预分配结点个数,然后就一次插入这些结点。源代码加注释如下:

     

       1: //poll为内存池指针,preallocate是预分配基数树的节点数目,如果传-1,那么将会根据当前系统一个页的大小来预分配基数树结点
       2: ngx_radix_tree_t *ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate)
       3: {
         
       4:     uint32_t           key, mask, inc;
       5:     ngx_radix_tree_t  *tree;
       6:  
       7:     //分配ngx_radix_tree_t
       8:     tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t));
       9:     if (tree == NULL) {
         
      10:         return NULL;
      11:     }
      12:  
      13:     tree->pool = pool;
      14:     tree->free = NULL;
      15:     tree->start = NULL;
      16:     tree->size = 0;
      17:  
      18:     //分配根节点
      19:     tree->root = ngx_radix_alloc(tree);
      20:     if (tree->root == NULL) {
         
      21:         return NULL;
      22:     }
      23:  
      24:     tree->root->right = NULL;
      25:     tree->root->left = NULL;
      26:     tree->root->parent = NULL;
      27:     tree->root->value = NGX_RADIX_NO_VALUE;
      28:  
      29:     //如果需要的预分配结点为0个,完成返回
      30:     if (preallocate == 0) {
         
      31:         return tree;
      32:     }
      33:  
      34:     /*
      35:      * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.
      36:      * increases TLB hits even if for first lookup iterations.
      37:      * On 32-bit platforms the 7 preallocated bits takes continuous 4K,
      38:      * 8 - 8K, 9 - 16K, etc.  On 64-bit platforms the 6 preallocated bits
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值