源码阅读笔记 - 1 MSVC2015中的std::sort

大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来

这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格式化,去掉或者展开用于条件编译或者debug检查的宏,依重要程度重新排序函数,但是不会改变命名方式(虽然MSVC的STL命名实在是我不能接受的那种),对于代码块的解释会在代码块前(上面)用注释标明。

template<class _RanIt, class _Diff, class _Pr> inline
void _Sort(_RanIt _First, _RanIt _Last, _Diff _Ideal, _Pr _Pred)
{
    /*
    sort的本体部分,解释下参数
    _First _Last是边界
    _Ideal是与递归深度挂钩的参数
    */
    _Diff _Count;
    for (; std::_ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal; )
    {
        /*
        _ISORT_MAX == 32
        数组长度<=32的时候或者递归深度 >1.5log2(n)的时候退出快速排序
        */
        std::pair<_RanIt, _RanIt> _Mid =
            _Unguarded_partition(_First, _Last, _Pred);
        _Ideal /= 2, _Ideal += _Ideal / 2;
        /*
        对整条序列进行划分,同时“递归深度倒计时”变为原来的0.75倍
        划分的代码在后面提到
        */
        if (_Mid.first - _First < _Last - _Mid.second)
        {
            _Sort(_First, _Mid.first, _Ideal, _Pred);
            _First = _Mid.second;
        }
        else
        {
            _Sort(_Mid.second, _Last, _Ideal, _Pred);
            _Last = _Mid.first;
        }
        /*
        只选择较短的序列递归进行快速排序
        对较长的序列,更新_First和_Last,继续循环
        */
    }

    if (std::_ISORT_MAX < _Count)
    {
        std::make_heap(_First, _Last, _Pred);
        std::sort_heap(_First, _Last, _Pred);
    }
    /*
    结束时的_Count仍然>32的情况,是递归过深的情况,进行堆排序
    堆排序就是简单的inplace堆排序,不多说了
    */
    else if (2 <= _Count)
    {
        _Insertion_sort(_First, _Last, _Pred);
    }
    /*
    序列过短而退出,进行简单的插入排序
    */
}

template<class _RanIt, class _Pr> inline
void _Med3(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred)
/*
三值取中,接受三个随机访问迭代器,和一个用于判断大小的谓词
结果将使_First, _Mid, _Last按_Pred定义的顺序排列,即中值处于_Mid的位置
*/
{
    if (_Pred(*_Mid, *_First))
    {
        std::iter_swap(_Mid, _First);
    }
    if (_Pred(*_Last, *_Mid))
    {
        std::iter_swap(_Last, _Mid);
        if (_Pred(*_Mid, *_First))
        {
            std::iter_swap(_Mid, _First);
        }
    }
    /*
    上面的代码本质上是进行了一个三元素的插入排序,按照插入排序的算法原理将循环展开为代码
    默认第一个元素排好序
    排第二个元素 - 和第一个比较大小 - 不满足_Pred序就交换
    前两个元素排序完毕
    排第三个元素 - 和第二个比较大小 - 不满足_Pred序就交换
    发生交换表明还有可能小于第一个元素,这时比较第一和第二两个元素
    */
}

template<class _RanIt, class _Pr> inline
void _Median(_RanIt _First, _RanIt _Mid, _RanIt _Last, _Pr _Pred)
/*
优化版的取中值程序,在数组长度足够大的时候,采用9值取中,更有机会取到最优的中值
*/
{
    if (40 < _Last - _First)
    {
        size_t _Step = (_Last - _First + 1) / 8;
        _Med3(_First, _First + _Step, _First + 2 * _Step, _Pred);
        _Med3(_Mid - _Step, _Mid, _Mid + _Step, _Pred);
        _Med3(_Last - 2 * _Step, _Last - _Step, _Last, _Pred);

        _Med3(_First + _Step, _Mid, _Last - _Step, _Pred);
        /*
        当数组长度大于40的时候,采用9值取中,这个40大概是实践得出的较好值吧
        个人猜测是如果数组长度小于40仍进行9值取中,得到的优秀的中值做减少的排序代价不足以抵消执行4次3值取中本身的代价

        前三个_Med3分别给数组头、中、尾相距_Step的三个元素排序
        第四个_Med3给排好序的头、中、尾三组数字的中值继续取中值
        结果使得9个值中的中值处于_Mid位置,
        */
    }
    else
    {
        _Med3(_First, _Mid, _Last, _Pred);
    }
}

template<class _RanIt, class _Pr> inline
std::pair<_RanIt, _RanIt> _Unguarded_partition(_RanIt _First, _RanIt _Last, _Pr _Pred)
{
    /*
    无保护的划分,这个划分与std::partition有本质上的不同
    划分考虑值等于pivot的情况,将整个序列分成3段,用一个pair返回他们之间的交界
    */
    _RanIt _Mid = _First + (_Last - _First) / 2;
    _Median(_First, _Mid, _Last - 1, _Pred);
    _RanIt _Pfirst = _Mid;
    _RanIt _Plast = _Pfirst + 1;
    /*
    这里是初始化部分:
    执行_Median之后,_Mid是pivot
    _Pfirst: == pivot的子序列的左边界
    _Plast: == pivot的子序列的右边界

    [_Pfirst, _Plast) == pivot
    */
    while (_First < _Pfirst
           && !_Pred(*(_Pfirst - 1), *_Pfirst)
           && !_Pred(*_Pfirst, *(_Pfirst - 1)))
    {
        --_Pfirst;
    }
    while (_Plast < _Last
           && !_Pred(*_Plast, *_Pfirst)
           && !_Pred(*_Pfirst, *_Plast))
    {
        ++_Plast;
    }
    /*
    进行初始的处理,尽可能的扩展==pivot子序列,这种情况针对大量元素==pivot的情况
    此处可以看出,对于已有的判断偏序关系的_Pred,判断两元素相等的情况是当_Pred(a, b)和_Pred(b, a)都不成立的时候
    */

    _RanIt _Gfirst = _Plast;
    _RanIt _Glast = _Pfirst;
    /*
    _Gfirst是大于pivot的值的终点
    _Glast是小于pivot的值的起点
    也即[_Plast, _Gfirst) > pivot
    [_Pfirst, _Plast) == pivot
    [_Glast, _Pfirst) < pivot
    这里命名很迷,提请各位注意

    STL的迭代器在一组操作之后,永远保证其左闭右开区间的正确性
    这一组操作不应该在操作之后对迭代器进行额外的加减以使其满足左闭右开的性质
    他们本身就应该是针对左闭右开区间设计的
    */

    for (; ; )
    {
        for (; _Gfirst < _Last; ++_Gfirst)
        {
            if (_Pred(*_Pfirst, *_Gfirst))
            {
                continue;
            }
            else if (_Pred(*_Gfirst, *_Pfirst))
            {
                break;
            }
            else if (_Plast++ != _Gfirst)
            {
                std::iter_swap(_Plast - 1, _Gfirst);
            }
        }
        /*
        把range约定复制过来方便看:

        [_First, _Glast) 未划分部分
        [_Glast, _Pfirst) < pivot
        [_Pfirst, _Plast) == pivot
        [_Plast, _Gfirst) > pivot
        [_Gfirst, _Last) 未划分部分

        _Pfirst指向==pivot子序列的第一个
        尝试左移_Gfirst迭代器,
        if   找到>pivot的元素,继续左移
        elif 找到<pivot的元素,停止,此时_Gfirst指向那个>pivot的元素
        else 找到=pivot的元素,这个元素和>pivot的第一个元素交换,同时=pivot序列右侧增长
        比如
        555568765
            ^   ^
        交换后
        555558766
             ^  ^
        第一个^是_Plast,第二个^是_Gfirst
        结束时,_Gfirst永远指向一个<pivot的元素(或者==_Last)
        符合我的说法:[_Plast, _Gfirst)是个 > pivot的左开右闭区间,_Gfirst恰好不满足>pivot
        */

        for (; _First < _Glast; --_Glast)
        {
            if (_Pred(*(_Glast - 1), *_Pfirst))
            {
                continue;
            }
            else if (_Pred(*_Pfirst, *(_Glast - 1)))
            {
                break;
            }
            else if (--_Pfirst != _Glast - 1)
            {
                std::iter_swap(_Pfirst, _Glast - 1);
            }
        }
        /*
        原理同上,左移_Glast
        结束时,_Glast指向一个>pivot的元素(或者==_First)
        */

        /*
        [_First, _Glast) 未划分部分
        [_Glast, _Pfirst) < pivot
        [_Pfirst, _Plast) == pivot
        [_Plast, _Gfirst) > pivot
        [_Gfirst, _Last) 未划分部分
        */

        if (_Glast == _First && _Gfirst == _Last)
        {
            return (pair<_RanIt, _RanIt>(_Pfirst, _Plast));
        }
        /*
        如果_Glast和_Gfirst都到达了终点,就返回,_Pfirst, _Plast代表什么上面有
        */

        if (_Glast == _First)
        {
            if (_Plast != _Gfirst)
            {
                std::iter_swap(_Pfirst, _Plast);
            }
            ++_Plast;
            std::iter_swap(_Pfirst++, _Gfirst++);
        }
        /*
        如果<pivot的部分已经排好,那么进行一次滚动交换,==pivot的第一个和>pivot的第一个交换,
        >pivot的区间左边界右移
        _Pfirst现在指向一个>pivot的元素,_Pfirst和_Gfirst交换,然后两个迭代器都右移
        比如
        1423245555556978672xxx
              ^     ^     ^
        第一次交换
        1423246555555978672xxx
              ^      ^    ^
        第二次交换
        1423242555555978676xxx
               ^     ^     ^
        */

        else if (_Gfirst == _Last)
        {	// no room at top, rotate pivot downward
            if (--_Glast != --_Pfirst)
            {
                std::iter_swap(_Glast, _Pfirst);
            }
            std::iter_swap(_Pfirst, --_Plast);
        }
        /*
        和上面相同,进行一次左侧的滚动交换,不多赘述
        */

        else
        {
            std::iter_swap(_Gfirst++, --_Glast);
        }

        /*
        左右两个迭代器都没有到达边界,直接交换并移动即可
        */
    }
}

template<class _RanIt, class _Pr> inline
void sort(_RanIt _First, _RanIt _Last, _Pr _Pred)
{
    _Sort(_Unchecked(_First), _Unchecked(_Last), _Last - _First, _Pred);
}
/*
sort的向外暴露包装,不再赘述
*/
template<class _RanIt> inline
void sort(_RanIt _First, _RanIt _Last)
{
    std::sort(_First, _Last, less<>());
}
/*
sort的向外暴露包装,不再赘述
*/

template<class _BidIt1, class _BidIt2> inline
_BidIt2 _Move_backward(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest, _Nonscalar_ptr_iterator_tag)
{	// move [_First, _Last) backwards to [..., _Dest), arbitrary iterators
    while (_First != _Last)
        *--_Dest = std::move(*--_Last);
    return (_Dest);
}

template<class _InIt, class _OutIt> inline
_OutIt _Move_backward(_InIt _First, _InIt _Last, _OutIt _Dest, _Scalar_ptr_iterator_tag)
{	// move [_First, _Last) backwards to [..., _Dest), pointers to scalars
    ptrdiff_t _Count = _Last - _First;
    ::memmove(&*_Dest - _Count, &*_First,
              _Count * sizeof(*_First));
    return (_Dest - _Count);
}
/*
针对POD类型和非POD类型的实现,其中POD类型使用memmove,更快,非POD类型使用std::move,相比C++9803更快
*/

template<class _BidIt1, class _BidIt2> inline
_BidIt2 _Move_backward(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest)
{	// move [_First, _Last) backwards to [..., _Dest), unchecked
    return (_Move_backward(_First, _Last,
                           _Dest, _Ptr_cat(_First, _Dest)));
}
/*
内存移动的包装
*/

template<class _BidIt, class _Pr, class _Ty> inline
void _Insertion_sort1(_BidIt _First, _BidIt _Last, _Pr _Pred, _Ty *)
{
    /*
    这部分的迭代器约定
    [_First, _Next) 排好的部分
    [_Next, _Last) 没排好的部分
    */
    if (_First != _Last)
    {
        for (_BidIt _Next = _First; ++_Next != _Last; )
        {
            _BidIt _Next1 = _Next;
            _Ty _Val = _Move(*_Next);

            if (_Pred(_Val, *_First))
            {
                _Move_backward(_First, _Next, ++_Next1);
                *_First = _Move(_Val);
                /*
                如果要插入的元素比已经排好的第一个元素还要小,那么直接把已经排好的部分右移,第一个元素放进去
                这里针对大部分已经排好的序列优化
                */
            }
            else
            {
                for (_BidIt _First1 = _Next1; _Pred(_Val, *--_First1); _Next1 = _First1)
                {
                    *_Next1 = _Move(*_First1);
                }
                *_Next1 = _Move(_Val);
                /*
                否则正常的挨个比较并移动
                */
            }
        }
    }
}

template<class _BidIt, class _Pr> inline
void _Insertion_sort(_BidIt _First, _BidIt _Last, _Pr _Pred)
{	// insertion sort [_First, _Last), using _Pred
    _Insertion_sort1(_First, _Last, _Pred, _Val_type(_First));
    /*
    插入排序不安全版实现的wrap
    */
}
时间: 2024-10-26 23:08:44

源码阅读笔记 - 1 MSVC2015中的std::sort的相关文章

CI框架源码阅读笔记3 全局函数Common.php

从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作). 打开Common.php中,第一行代码就非常诡异: if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 上一篇(CI框架源码阅读笔记2 一切的入口 index

CI框架源码阅读笔记5 基准测试 BenchMark.php

上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功能,各模块之间可以相互调用,共同构成了CI的核心骨架. 从本篇开始,将进一步去分析各组件的实现细节,深入CI核心的黑盒内部(研究之后,其实就应该是白盒了,仅仅对于应用来说,它应该算是黑盒),从而更好的去认识.把握这个框架. 按照惯例,在开始之前,我们贴上CI中不完全的核心组件图: 由于BenchMa

CI框架源码阅读笔记2 一切的入口 index.php

上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里这次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中,我们并不会逐行进行解释,而只解释核心的功能和实现. 1.       设置应用程序环境 define('ENVIRONMENT', 'development'); 这里的development可以是任何你喜欢的环境名称(比如dev,再如test),相对应的,你要在下面的switch case代码块中

Apache Storm源码阅读笔记

欢迎转载,转载请注明出处. 楔子 自从建了Spark交流的QQ群之后,热情加入的同学不少,大家不仅对Spark很热衷对于Storm也是充满好奇.大家都提到一个问题就是有关storm内部实现机理的资料比较少,理解起来非常费劲. 尽管自己也陆续对storm的源码走读发表了一些博文,当时写的时候比较匆忙,有时候衔接的不是太好,此番做了一些整理,主要是针对TridentTopology部分,修改过的内容采用pdf格式发布,方便打印. 文章中有些内容的理解得益于徐明明和fxjwind两位的指点,非常感谢.

CI框架源码阅读笔记4 引导文件CodeIgniter.php

到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.com/usr/reg 经过引导文件,实际上会交给Application中的UsrController控制器的reg方法去处理. 这之中,CodeIgniter.php做了哪些工作?我们一步步来看. 1.    导入预定义常量.框架环境初始化 之前的一篇博客(CI框架源码阅读笔记2 一切的入

IOS测试框架之:athrun的InstrumentDriver源码阅读笔记

athrun的InstrumentDriver源码阅读笔记 作者:唯一 athrun是淘宝的开源测试项目,InstrumentDriver是ios端的实现,之前在公司项目中用过这个框架,没有深入了解,现在回来记录下. 官方介绍:http://code.taobao.org/p/athrun/wiki/instrumentDriver/ 优点:这个框架是对UIAutomation的java实现,在代码提示.用例维护方面比UIAutomation强多了,借junit4的光,我们可以通过junit4的

Yii源码阅读笔记 - 日志组件

?使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category);Yii::trace($message, $category); 两者的区别在于后者依赖于应用开启调试模式,即定义常量YII_DEBUG: defined('YII_DEBUG') or define('YII_DEBUG', true); Yii::log方法的调用需要指定message的level和category.category是格式为“xxx.yyy.z

Spark源码阅读笔记之Broadcast(一)

Spark源码阅读笔记之Broadcast(一) Spark会序列化在各个任务上使用到的变量,然后传递到Executor中,由于Executor中得到的只是变量的拷贝,因此对变量的改变只在该Executor有效.序列化后的任务的大小是有限制的(由spark.akka.frameSize决定,值为其减去200K,默认为10M-200K),spark会进行检查,超出该限制的任务会被抛弃.因此,对于需要共享比较大的数据时,需要使用Broadcast. Spark实现了两种传输Broadcast的机制:

jdk源码阅读笔记之java集合框架(二)(ArrayList)

关于ArrayList的分析,会从且仅从其添加(add)与删除(remove)方法入手. ArrayList类定义: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Monaco } span.s1 { color: #931a68 } public class ArrayList<E> extends AbstractList<E> implements List<E> ArrayList基本属性: /** *