zoukankan      html  css  js  c++  java
  • 快速排序 Gnu glibc qsort_r

    今天,又重新看了一下GLIBC中快速排序的源码~ 又多了一些不同的理解,为什么这篇文章迟迟没有

    发布,也是因为对于源码没有深刻的理解,感觉很多点都不明白.今天就找了些资料,仔细揣摩了一遍源

    码,索性就写出来,有不到之处,请批评指正~

    之前一直都是以为GLIBC中的源码应该会花费大量的代码做通用处理,但是今天重新浏览之后,发现自己

    错了,哎~  不过还是蛮高兴的,毕竟还年轻嘛~

    其实,GLIBC中快速排序的源码重点在榨取CPU的性能,提高快排的执行效率.为什么说是榨取呢? 因为

    源码真的将利用效率做到了极致.

    1   /* For large object sizes use indirect sorting.  */
    2   if (s > 32)
    3     size = 2 * n * sizeof (void *) + s; // 这里为什么要计算2N+1的空间?

     看到前面的英文注释了吧,源码中对于快速排序也做了不同的分类,采用不同的方式.意思就是:如果单个元素

    长度大于32BYTE,那么就视为大对象,采用间接排序.至于间接排序是什么意思?下面再说.

    不知道有没有同学会不理解为什么会需要sizeof(void *).这个问题嘛,只能说脑袋总是有闹别扭的时候.

    浪费一点时间回头看看qsort_r函数声明.

    1 void
    2 qsort_r (void *b, size_t n, size_t s, __compar_d_fn_t cmp, void *arg)

     可以很清楚的看到,将目标数组参数的类型声明为void *,所以这里是需要计算需要的存储空间大小.然后联系

    源码中采用的两种排序方式,间接快速排序和直接快速排序,如果只是单凭元素大小来选择排序方式,那是不可靠

    的,为什么? 因为即使元素占用空间很大,但是数量很少还是不能采用间接排序,所以判断需要排序的目标数组的

    长度才是最可靠地依据.(修正,其实这里指的是指针的长度,看没看出错误呢?广大同学们. -,-)

    1   if (size < 1024)
    2     /* The temporary array is small, so put it on the stack.  */
    3     p.t = __alloca (size);

    所以呢,上面的代码就可以说明一切了.

    __alloca是什么?我们看看哈~ 它是一个宏:

    1 #ifdef    __GNUC__
    2 # define __alloca(size)    __builtin_alloca (size)
    3 #endif /* GCC.  */

     这是GCC特有的一个函数实现,查阅了很多的资料.手册中说_builtin_alloca是一个私有的实现版本.

    其他的并没有多作说明. 其他的资料暂时也没有找到,知道的可以告诉我哈~

    这几段代码告诉我们,若目标数组长度小于1024BYTE,那么就使用直接快速排序,否则就是用间接快速排序.

    1       if (size / pagesize > (size_t) phys_pages)
    2     {
    3       _quicksort (b, n, s, cmp, arg);
    4       return;
    5     }

    先跳过前面的几段代码,直接看看上面的这段代码,这里调用了直接快速排序,我们先不关心直接排序的实现,

    先看看为什么会调用直接排序.注意一下条件. 需要弄懂这个条件,就需要看前面一段忽略的代码.

     1       /* We should avoid allocating too much memory since this might
     2      have to be backed up by swap space.  */
     3       static long int phys_pages;
     4       static int pagesize;
     5 
     6       if (pagesize == 0)
     7     {
     8       phys_pages = __sysconf (_SC_PHYS_PAGES);
     9 
    10       if (phys_pages == -1)
    11         /* Error while determining the memory size.  So let's
    12            assume there is enough memory.  Otherwise the
    13            implementer should provide a complete implementation of
    14            the `sysconf' function.  */
    15         phys_pages = (long int) (~0ul >> 1);
    16 
    17       /* The following determines that we will never use more than
    18          a quarter of the physical memory.  */
    19       phys_pages /= 4;
    20 
    21       /* Make sure phys_pages is written to memory.  */
    22       atomic_write_barrier ();
    23 
    24       pagesize = __sysconf (_SC_PAGESIZE)

     这段代码涉及到的函数比较的复杂,在本文中将不做详细解释,简单解释一下.这段代码是获取系统的

     物理可用内存参数. 最主要的是避免使用交换空间,因为那样做会降低排序的效率.

    简单解释了一下这段代码,前面的条件也就清晰明了了,如果系统的可用内存够支配,那么就从堆分配

    内存采用间接快速排序.如果不够用了,那么就只好直接快速排序了.也就是直接调用_quicksort函数.

     1       int save = errno;
     2       tmp = malloc (size);
     3       __set_errno (save);
     4       if (tmp == NULL)
     5     {
     6       /* Couldn't get space, so use the slower algorithm
     7          that doesn't need a temporary array.  */
     8       _quicksort (b, n, s, cmp, arg);
     9       return;
    10     }
    11       p.t = tmp;

    使用malloc函数从堆分配所需内存空间,如果无法分配(无法分配的原因很多),那么依然采用间接快速排序.

    如果分配了内存,那么将空间地址指针放入一个结构体中.看看这个结构体:

    1 struct msort_param
    2 {
    3   size_t s;
    4   size_t var;
    5   __compar_d_fn_t cmp;
    6   void *arg;
    7   char *t;
    8 };
    1   p.s = s;
    2   p.var = 4;
    3   p.cmp = cmp;
    4   p.arg = arg;

    下面这段初始化的源码,初始化p中的成员. 对照着前面的结构体声明,各个参数的类型.

     1 if (s > 32)  // 如果单个元素长度>32
     2     {
     3       /* Indirect sorting.  */
     4       char *ip = (char *) b; // 获取目标数组的地址.
     5       void **tp = (void **) (p.t + n * sizeof (void *)); // 获取排序空间第N+1位置指针.
     6       void **t = tp;
     7       void *tmp_storage = (void *) (tp + n); // 排序空间的最后一个位置,申请的空间大小是2N+1.
     8  // 注意这里为什么要使用void *,因为间接排序是对指针进行排序,而不是针对元素.
     9       while ((void *) t < tmp_storage)
    10     {
    11       *t++ = ip; // 拷贝指针.
    12       ip += s;   // 移动到下一个指针. 因为类型是字节,所以要按照元素长度进行移位.
    13     }
    14       p.s = sizeof (void *);
    15       p.var = 3;
    16       msort_with_tmp (&p, p.t + n * sizeof (void *), n);
    17 
    18       /* tp[0] .. tp[n - 1] is now sorted, copy around entries of
    19      the original array.  Knuth vol. 3 (2nd ed.) exercise 5.2-10.  */
    20       char *kp;
    21       size_t i;
    22       for (i = 0, ip = (char *) b; i < n; i++, ip += s)
    23     if ((kp = tp[i]) != ip)
    24       {
    25         size_t j = i;
    26         char *jp = ip;
    27         memcpy (tmp_storage, ip, s);
    28 
    29         do
    30           {
    31         size_t k = (kp - (char *) b) / s;
    32         tp[j] = jp;
    33         memcpy (jp, kp, s);
    34         j = k;
    35         jp = kp;
    36         kp = tp[k];
    37           }
    38         while (kp != ip);
    39 
    40         tp[j] = jp;
    41         memcpy (jp, tmp_storage, s);
    42       }
    43     }
    44   else
    45     {
    // 下面的源码就没什么了,主要是依照条件初始化p.var
    46 if ((s & (sizeof (uint32_t) - 1)) == 0 47 && ((char *) b - (char *) 0) % __alignof__ (uint32_t) == 0) 48 { 49 if (s == sizeof (uint32_t)) 50 p.var = 0; 51 else if (s == sizeof (uint64_t) 52 && ((char *) b - (char *) 0) % __alignof__ (uint64_t) == 0) 53 p.var = 1; 54 else if ((s & (sizeof (unsigned long) - 1)) == 0 55 && ((char *) b - (char *) 0) 56 % __alignof__ (unsigned long) == 0) 57 p.var = 2; 58 } 59 msort_with_tmp (&p, b, n); 60 } 61 free (tmp);

    上面的注释中说明了,很大一段代码都是依照条件去初始化p中var的值,在源码中,可以看到,

    p.var有四个值,分别是0,1,2,3. 分别代表什么意思呢? 要到msort_with_tmp函数的源码中

    去看看才知道.同样,想要搞明白,间接快速排序时如何做的,也要进去看看.

    下面的分析,留在下一篇文章中再续吧,有事处理一下~

     
  • 相关阅读:
    状态图(Statechart Diagram)—UML图(五) .
    时序图(Sequence Diagram)—UML图(六)
    传说中的WCF(8):玩转消息协定
    SCSF 系列:利用 Smart Client Software Factory 实现 StopLight (Step By Step)
    ASP.NET MVC 3.0(四): 我要MVC潜规则之配置Routing
    传说中的WCF(14):WCF也可以做聊天程序
    部署图(Deployment Diagram)—UML图(九)
    活动图(Activity Diagram)—UML图(四)
    今天做的机试题Socket聊天程序
    UML的基本结构 .
  • 原文地址:https://www.cnblogs.com/respawn/p/2723257.html
Copyright © 2011-2022 走看看