关于STL中stack的实现的讨论

Cortex-M FPU的Lazy Stacking机制 By: Ailson Jack Date: 2020.08.15 个人博客:http://www.only2fire.com/ 本文在我博客的地址是:http://www.only2fire.com/archives/121.html,排版更好,便于学习,也可以去我博客逛逛,兴许有你想要的内容呢。 1.浮点运算指令 浮点运算指令用于FPU单元的单精度浮点运算。浮点运算指令都是用V开头的汇编指令。 只有在FPU开启的状态下,才能运行这些指令。 如果在FPU没有开启的状态下,执行... 阅读详情
 

关于STL中stack的实现的讨论

分类: 代码优化   2435人阅读  评论(5)  收藏  举报
                              关于STL中stack的实现的讨论
                          
HouSisong@GMail.com   2004.11.07整理

tag:STL,stack,faststack

2012.02.18 修正lazylazypig发现的my_fast_stack::size()的bug; empty()函数也稍做优化(作用不大);

摘要: 文章讨论了为什么大多数STL的stack的实现中,对于内部的容器默认选择deque容器;并且给出了自己的几个不同想法实现的stack;并进行了简单的性能比较测试;(文章最后给出了一个性能、特性都无懈可击的stack的实现!)

  文章来源于abp论坛中的一篇讨论帖子: http://bbs.allaboutprogram.com/viewtopic.php?t=1026这是自己开始接触泛型和STL时形成的一篇讨论;文章中借用了Elminster,papercrane,Innocentius,PolyRandom等人的部分观点

1:为什么大多数STL的stack的实现中,对于内部的容器默认选择deque容器?而不是vector?  
    STL中,stack对内部使用容器的函数调用主要有:push_back,back,pop_back等,也就是顺序容器都满足要求(包括vector,deque,list)。很多人应该和我一样,在STL之前看到的stack实现都是以动态数组来(甚至静态数组)实现为主,也就是接近于使用vector方案;那为什么STL偏偏选择deque呢!?
    我的分析:
    a.用vector实现中(push_back动作为分期摊还常数时间),如果发生容器的大小改变时,将可能产生一个大动作(申请空间,拷贝构造,释放原来的元素和空间,该动作成线性复杂度) 而且vector的很多实现版本中,容器在任何情况下都从不缩减已经申请的空间容量(swap技巧除外); 
    b.用deque实现时,容器的大小改变时(数据量较大),动作比vector就小多了(常数复杂度),并且当容器的大小变小时,还可以适当减小容量;但push_back 的逻辑相对vector复杂一点;
    c.用list实现时,不用考虑空间容量变化;但每次的压入弹出开销(内存时间)较大,但很平稳;那么,经过分析,在不同的应用场合,为stack选择不同的内部容器是很有必要的;如果对stack有性能上的要求,就应该考虑这一点(甚至重新写一个最适应问题要求的stack); 比如:要求有最快的平均访问速度,而且大概的容量要求也清楚(比较衡定),那么,使用vector是个不错的选择 要求每次的访问时间平稳,而不在乎平均访问时间时,那么,可以考虑使用list;所以,库默认的deque是个不错的选择,它介于vector和list之间,并且很好的综合了两者的优势;另papercrane:“oncrete policy deque相对于stack来说就像傻瓜机,乱用也不会有什么太大的问题。如你所说的平均时间和最差时间的要求,我觉得就好像hash map和tree map的性能差别一样。 ”

    (提示:文章后面还有两种想进一步融合这三种方式各自优势的stack的实现,特别是最后那个实现也许推翻了这里的表面上得到的看法);


2.自己也来写一个stack;
  由于看到VC6中实现的太差,所以自己简单写了一个stack模版实现,性质比较接近于stack<T,vector<T> >, 代码如下:

template<class T>  
class
 mystack 
//
测试用 
//
没有考虑异常时的rollback语义 
//另: Elminster指出“使用 count 不是一个好主意。保存一个指向“下一个位置”的指针应该会效率更高,而且也更易读”;

{ 
public
: 
    typedef T               value_type; 
    typedef unsigned int
      size_type; 
    mystack():_lenght(0),_count(0
) {} 
    ~
mystack() 
   { 
      if (_lenght!=0
) 
      { 
         for (int i=0;i<_count;++
i) 
         { 
            ((T*)(&_vData[i*sizeof(T)]))->T::~
T(); 
         } 
      }
   } 
    bool empty() const
    
      { return (0==
_count); } 
    size_type size() const
 
      { return
 _count; } 
    value_type&
 top() 
      { return *(T*)(&_vData[(_count-1)*sizeof
(T)]); } 
    const value_type& top() const
 
      { return *(T*)(&_vData[(_count-1)*sizeof
(T)]); } 
    void push(const value_type&
 x) 
   { 
      if (_count>=
_lenght) 
      { 
         _resize(); 
      } 
      new ((T*)(&_vData[_count*sizeof
(T)])) T(x) ; 
      ++
_count; 
   } 
    void
 pop() 
   { 
      --
_count; 
      ((T*)(&_vData[_count*sizeof(T)]))->T::~
T(); 
   } 
protected
: 
   std::vector<unsigned char>
   _vData; 
   size_type      _lenght; 
   size_type      _count; 
   void
 _resize() 
   { 
      if (0==
_lenght) 
      { 
         _lenght=32
; 
         _vData.resize(_lenght*sizeof
(T)); 
      } 
      else
 
      { 
         _lenght*=2
; 
         std::vector<unsigned char>
 new_vData; 
         new_vData.resize(sizeof(T)*
_lenght); 
         for (int i=0;i<_count;++
i) 
         { 
            T& x=*(T*)(_vData.begin()+i*sizeof
(T)); 
            new ((T*)(new_vData.begin()+i*sizeof
(T))) T(x); 
            (&x)->T::~
T(); 
         } 
         _vData.swap(new_vData); 
      } 
   } 
}; 


测试环境:VC6,赛扬1G,256M内存 
测试代码:(不好意思,代码风格被VC的环境影响太久,想改变这种风格ing) 
另: Elminster指出测试代码里面,TestStack("stack_deque_T0 : ",stack_deque_T0()) 这个做法也不太妥当。TestStack 的第二个参数是 stackT&,把一个临时对象绑在非常量引用上可能会带来问题。

 typedef  int Test0_T; // 简单 POD 类型 

class Test1_T    // 较复杂的类 
{ 
public: 
    char _c; 
    double _d; 
    int  _i; 
    char* _p; 
   Test1_T():_c(),_d(),_i(),_p( new  char[20]) 
      {} 
   Test1_T( const Test1_T& x) 
      :_c(),_d(),_i(),_p( new  char[20]) 
      { (* this). operator =(x);  } 
   ~Test1_T(){ delete[] _p; } 
   Test1_T&  operator =( const Test1_T& x) 
   { 
      _c=x._c; _d=x._d; _i=x._i; 
       for ( int i=0;i<20;++i) 
         _p[i]=x._p[i]; 
       return * this; 
   } 
}; 

__declspec( naked ) __int64 CPUCycleCounter() // 获取当前CPU周期计数(CPU周期数)
{
 __asm
 {
  RDTSC     // 0F 31   // eax,edx
  ret
 }
}

template< class stackT> 
int testProc(stackT& s, int Count) 
{ 

   typename stackT::value_type vl=stackT::value_type(); 
   typename stackT::value_type vx; 

   __int64   t0=::CPUCycleCounter(); // 返回CPU启动以来运行的周期数 

    for ( int c=0;c<100;++c) 
   { 
       int i; 
       for (i=0;i<Count;++i) 
         s.push(vl); 
       for (i=0;i<Count;++i) 
         vx=s.top(); 
       for (i=0;i<Count;++i) 
         s.pop(); 
   } 

   __int64   t1=::CPUCycleCounter(); 

    return   int((t1-t0)/100); 
} 

template< class stackT> 
CString TestStack(PCSTR lab,stackT& s) 
{ 
    int   t0=testProc(s,10); 
    int   t1=testProc(s,1000); 
    int   t2=testProc(s,100000); 

   CString str; 
   str.Format("%10d,%10d,%10d",t0,t1,t2); 
   str+= char(13);str+= char(10); 
    return lab+str; 
} 


void CSTACKTESTDlg::OnBUTTONTest() 
{ 
    //  TODO: Add your control notification handler code here 
    using  namespace std; 
   typedef stack<Test0_T,deque<Test0_T> >   stack_deque_T0; 
   typedef stack<Test1_T,deque<Test1_T> >   stack_deque_T1; 
   typedef stack<Test0_T,vector<Test0_T> >   stack_vector_T0; 
   typedef stack<Test1_T,vector<Test1_T> >   stack_vector_T1; 
   typedef stack<Test0_T,list<Test0_T> >   stack_list_T0; 
   typedef stack<Test1_T,list<Test1_T> >   stack_list_T1; 
   typedef mystack<Test0_T>            mystack_T0; 
   typedef mystack<Test1_T>            mystack_T1; 

   CString str; 
   str+=TestStack("stack_deque_T0 : ",stack_deque_T0()); 
   str+=TestStack("stack_vector_T0: ",stack_vector_T0()); 
   str+=TestStack("stack_list_T0  : ",stack_list_T0()); 
   str+=TestStack("mystack_T0     : ",mystack_T0()); 
   str+= char(13);str+= char(10); 
   str+=TestStack("stack_deque_T1 : ",stack_deque_T1()); 
   str+=TestStack("stack_vector_T1: ",stack_vector_T1()); 
   str+=TestStack("stack_list_T1  : ",stack_list_T1()); 
   str+=TestStack("mystack_T1     : ",mystack_T1()); 

    this->m_str=str; // ::MessageBox(0,str,"",0); 
   UpdateData(FALSE); 
}

测试结果:(不同使用环境下的测试情况可能不同,该数据仅作参考) 
(另:测试中使用的STL是VC6自带的)

(计时单位:万CPU周期) 
N: 10 1000 100000 
stack_deque_T0  : 2695,  65621,  12191532 
stack_vector_T0 :  579,  47750,   9169942 
stack_list_T0   : 6028, 957515, 167660924 
mystack_T0      :  230,  13766,   2550403 (效果很好嘛)

stack_deque_T1 : 10699, 1168983, 270182336 
stack_vector_T1:  8043, 1247187, 250648378 
stack_list_T1  : 13043, 1988924, 424801657 
mystack_T1     :  6796, 1240879, 252690706(对于复杂对象,与stack_vector_T1的差不多)

Elminster给出了他的测试结果:
(计时单位:tick count) :
stack_deque_T0  : 0 156 12969 
stack_vector_T0 : 0  63  7562 
mystack_T0      : 0  47  6766

stack_deque_T1  : 16 500 57765 
stack_vector_T1 :  0 813 91843 
mystack_T1      :  0 734 85469

环境是 amd athlon 1600+, win2k sp4, 256M ddr, vs.net 2003,最大速度优化。

Elminster:“结论比较有趣。对于拷贝动作比较轻量级的 T0,你的方案比 deque 和 vector 都快,但与 vector 相差不大。此时 deque 的性能落的比较后面,原因应该是 deque 的 push_back 的逻辑相对复杂(我看了看)。对于拷贝动作比较重的 T1 ,你的方案和 vector 反而要比 deque 慢。这里的原因应该是 resize 的时候拷贝的开销太重。其实我认为对于 stack 的行为模式,类似 deque 的存储结构会比较好,因为空间完全不需要连续,像vector 那样需要拷贝的 resize 是毫无必要的。你自己实现一个简洁的 deque style 数据结构,相信可以把stack 的性能再提升一个台阶。”

/////////////////////////////////////////
对上面自己写的stack做了些改进:
测试环境:VC6,XP,赛扬1G,256M内存 
测试结果:

 template< class T> 
class mystack 
{ 
public: 
    enum{ type_sizes= sizeof(T) }; 
    typedef T               value_type; 
    typedef unsigned  int      size_type; 
    mystack():_begin(0),_end(0),_last(0) {} 
    ~mystack() 
   { 
       if (size()>0) 
      { 
          for (T* i=_begin;i<_end;++i) 
            i->T::~T(); 
      } 
       if (_begin!=0) delete[] (unsigned  char*)_begin;
   } 
     bool empty()  const    
      {  return (0==size()); } 
    size_type size()  const 
      {  return (_end-_begin); } 
    value_type& top() 
      {  return *(_end-1); } 
     const value_type& top()  const 
      {  return *(_end-1); } 
     void push( const value_type& x) 
   { 
       if (_end<_last) 
      { 
          new (_end) T(x) ; 
         ++_end; 
      } 
       else 
      { 
         _resize(); 
          new (_end) T(x); 
         ++_end; 
      } 
   } 
     void pop() 
   { 
      --_end; 
      _end->T::~T(); 
   } 
protected: 
   T*            _begin; 
   T*            _end; 
   T*            _last; 
    void _resize() 
   { 
       if (0==_begin) 
      { 
          const unsigned  int lenght=32; 
         _begin=(T*)(  new unsigned  char[lenght*type_sizes]); 
         _end  =_begin; 
         _last =_begin+lenght; 
      } 
       else 
      { 
         unsigned  int old_size=size(); 
         unsigned  int lenght=(old_size<<1); 
         T* _pNewData=(T*)(  new unsigned  char[lenght*type_sizes]); 
 

          int i=0; 
          try 
         { 
             for (;i<( int)old_size;++i) 
                new (_pNewData + i) T(*(_begin+i)); 
         } 
          catch(...) // rollback语义 
         { 
             for ( int r=0;r<i;++r) 
               (_pNewData + r)->T::~T(); 
            delete[] (unsigned  char*)_pNewData; 
             throw; 
         } 

          for ( int j=0;j<( int)old_size;++j) 
            (_begin+j)->T::~T(); 
         T* old_begin=_begin; 
         _begin=_pNewData; 
         _end  =_begin+old_size; 
         _last =_begin+lenght; 
         delete[] (unsigned  char*)old_begin; 
      } 
   } 
};

stack_deque_T0  : 2704,   65916,  11726143 
stack_vector_T0 :  581,   45713,   9192746 
stack_list_T0   : 5941, 1010118, 181184020 
mystack_T0      :  172,   12210,   2590757

stack_deque_T1  : 10587, 1962722, 255087740 
stack_vector_T1 :  8145, 1237245, 243123956 
stack_list_T1   : 13128, 2005212, 398256062 
mystack_T1      :  7127, 1173165, 251515265

测试环境:VS.net,XP,赛扬1G,256M内存 
stack_deque_T0  :  525,   33407,   6468531 
stack_vector_T0 :  574,   34710,   5236028 
stack_list_T0   : 4753, 1060449, 165353094 
mystack_T0      :  160,   10200,   2290310

stack_deque_T1  :  7470,  963957, 266757848 
stack_vector_T1 :  8412, 1275307, 278246615 
stack_list_T1   : 12001, 1993198, 475480319 
mystack_T1      :  7317, 1209379, 262822216

对比VC6, vs.net的 deque的性能好像提高了不少,但和vector一样还有优化的空间

3.利用x86的虚拟空间地址原理来实现stack
    deque的实现中一般内部维护一个动态指针数组,这些指针指向数据块(每块保存多个元素),这些块在内存中是不连续的,然而deque利用软件的方式提供了一个对外的线性访问的假象; 
    看一下现在的x86CPU,也有一个机制和这很像,即:保护的虚拟地址内存模型; 它把不连续的物理内存映射为一维线性内存模型,由于是硬件支持的映射,所以这种机制不会损失任何性能(deque、stack、vector等的实现也可以利用这一点)
    实现stack时,可以先开辟很大一块内存空间(足够),但不一次全部提交,开始只提交很少 的一部分物理页面,当需要增大容器容量时,只需要再提交部分物理页面给它,当需要减少容器容量时 可以收回部分页面;这个方案很像是deque的实现,但它可以获得vector式的线性内存访问能力和性能; 
    当然这会占用较大量的虚拟内存地址空间,这个方案也可能和具体平台相关,但不会浪费真正的物理内存空间(如果是64位CPU,那么虚拟内存地址空间的浪费就可以不用考虑了:))


新的容器ExStack,它同时具有vector的线性访问能力,和deque的内存管理方式,所以我期待它具有这两者的性质: 

class TAlloc // 移植时 在不同的平台下需要改变的部分 
{ 
public: 
    static  void* reserve(unsigned  int size)          // 申请保留虚拟空间地址 
   { 
       return ::VirtualAlloc(0,size,MEM_RESERVE,PAGE_READWRITE); 
   } 
    static  bool commit( void* pbase,unsigned  int offset,unsigned  int size)    // 提交物理空间 
   { 
       return 0!=::VirtualAlloc(((BYTE*)pbase)+offset,size,MEM_COMMIT,PAGE_READWRITE); 
   } 
    static  bool free( void* pbase)  // 解除提交的物理空间,并释放申请的虚拟空间地址 
   { 
       return 0!=::VirtualFree(pbase,0,MEM_RELEASE); 
   } 
}; 

 

template< class T> 
class ExStack 
{ 
public: 
    enum{ type_sizes= sizeof(T) };
    typedef T               value_type; 
    typedef unsigned  int      size_type; 
    ExStack():_begin(0),_end(0),_last(0) {} 
    ~ExStack() 
   { 
       if (size()>0) 
      { 
          for (T* i=_begin;i<_end;++i) 
            i->T::~T(); 
      } 
       if (_begin!=0) TAlloc::free(_begin); 
   } 
     bool empty()  const    
      {  return (0==size()); } 
    size_type size()  const 
      {  return (_end-_begin); } 
    value_type& top() 
      {  return *(_end-1); } 
     const value_type& top()  const 
      {  return *(_end-1); } 
     void push( const value_type& x) 
   { 
       if (_end<_last) 
      { 
          // std::_Construct(_end,x) ; 
          new (_end) T(x) ; 
         ++_end; 
      } 
       else 
      { 
         _resize(); 
          // std::_Construct(_end,x); 
          new (_end) T(x) ; 
         ++_end; 
      } 
   } 
     void pop() 
   { 
       // 没有实现收回物理内存的语义
      --_end; 
      _end->T::~T(); 
   } 
protected: 
   T*            _begin; 
   T*            _end; 
   T*            _last; 
    void _resize() 
   { 
       if (0==_begin) 
      { 
          const unsigned  int lenght=4*1024; // 4KB 边界对齐 
         _begin=(T*)TAlloc::reserve(256*1024*1024); // 预留地址空间 256 MB 
         TAlloc::commit(( void*)_begin,0,lenght*type_sizes); 
         _end  =_begin; 
         _last =_begin+lenght; 
      } 
       else 
      { 
         unsigned  int old_size=size(); 
         unsigned  int lenght=(old_size<<1); 
         TAlloc::commit(_begin,old_size*type_sizes,(lenght-old_size) 

*type_sizes); 
         _last =_begin+lenght; 
      } 
   } 
};
//测试条件更加接近于一般使用环境, 测试也更合理
int testProc(stackT& s, int Count) // 注意测试条件变了很多!!! 
{ 
 

   typename stackT::value_type vl=stackT::value_type(); 
   typename stackT::value_type vx; 

   __int64   t0=::CPUCycleCounter(); // 返回CPU启动以来运行的周期数 

    for ( int c=0;c<100;++c) 
   { 
      stackT temps; //     
       int i; 
       for (i=0;i<Count+1;++i) // +1 
         temps.push(vl); 
       for (i=0;i<Count;++i) 
         vx=temps.top(); 
       for (i=0;i<Count-1;++i) // -1 
         temps.pop(); 
       for (i=0;i<(Count>>1);++i) // ! 
         temps.push(vl); 
   } 

   __int64   t1=::CPUCycleCounter(); 

    return   int((t1-t0)/100); 
}
测试环境:XPvc.net赛扬1GHz256MB

测试结果:

            N  =         10       1000     100000 
stack_deque_T0 :       2942,    205538,  54123660 
stack_vector_T0:       4903,     66692,  21136702 
stack_list_T0  :       8003,   1232967, 239859174 
mystack_T0     :       1120,     20431,  12025250 
ExStack_T0     :      40710,     75998,   5908666

stack_deque_T1 :      18444,   2383833, 490387023 
stack_vector_T1:      35764,   3281555,1130011712 
stack_list_T1  :      19624,   2842182, 588527582 
mystack_T1     :      11764,   2285440, 749906486 
ExStack_T1     :      56646,   1680889, 372481178

mystack: 在大多数情况下都很优秀,但当数据量较大并且数据类型较复杂时,性能迅速下降std::stack<T,vector<T> >: 性质与mystack一致,但VC6版实现得太差了;(这种库看了就让人生气)std::stack<T,list<T> >: 绝对速度(平均速度)没法和其他实现比,但他的优点不在这;ExStack: 初始化和销毁开销太大了些(否则ExStack在很多测试中都将领先),数据量较大时,不出意料的在简单和复杂数据类型中都领先于对手(这时才显示出综合了vector与deque的优势);但是ExStack的适用范围实在太小了,比我预期的适用范围差;

(PolyRandom:我觉得这个ExStack不错,而且可以用的地方应该不少。)


4. 极速stack的诞生myfast_stack

    一次尝试(前奏):   还是忍不住自己写了一个deque内存管理方式的_myfast_stack;性质接近于std::static<T,deque<T> >; 其实比我想象中简单多了,很快就实现出来了(因为不需要实现一个完整的deque),测试时各项性能也很优秀;只是在“简单数据类型、元素个数N很小”时才输给了vector内存管理方式实现的mystack! 这一点好像在意料之中。

    是否这就是极限了呢?我准备把源码和测试结果发布出来的时候,却突然有了新的想法... 

一般deque内存管理需要用一个动态数组来保存指向数据块的指针,因为deque要求随机访问能力;但stack访问时明显没有随机访问特性的要求,所以 保存这些指针的数据结构最低需求也是满足stack接口就足够了;进一步的改进方案出来了,先用一个list来管理这些数据块,再把list的自己的数据成员与需要管理的数据空间合成放在一起(放在同一个数据块上);


哈哈,综合性能新的明星myfast_stack诞生了; 让人不敢相信的测试结果!!!


测试环境:Win2000,VC6,赛杨466,128M ( 自己的老古董电脑 )

             N =         20       1000      50000 
stack_deque_T0 :        434,      9355,    494481 
stack_vector_T0:        509,     11770,    583668 
stack_list_T0  :       1330,     82371,   7071753 
mystack_T0     :        103,      3248,    323565 
ExStack_T0     :       3258,      5580,    163168 
myfast_stack_T0:         97,      2682,    190075

stack_deque_T1 :       2464,    135898,  11981644 
stack_vector_T1:       4042,    228555,  22766841 
stack_list_T1  :       3281,    207232,  16723651 
mystack_T1     :       1916,    206413,  21782916 
ExStack_T1     :       5228,    129881,  10933717 
myfast_stack_T1:       1983,    124296,  10399781


实现的实质还是deque方式的,不管从那方面来看我认为它都可以将其他几个vector和list等的实现淘汰掉!

!!!myfast_stack太恐怖了,几乎没有缺陷!!!

///////myfast_stack源代码//////////
// 管理内存的list 
template<unsigned  int byte_size> 
class Tdata_list // 管理myfast_stack的内存 
{ 
    struct TNode // 节点类型 
   { 
      TNode*         pPrev; 
      TNode*         pNext; 
      unsigned  char   Data[byte_size]; // 数据空间 
   }; 
public: 
   Tdata_list():pNodeBegin(0),pNodeCur(0),_size(0){} 
   unsigned  int size()  const {  return _size; } 
    void push() // 配置空间 
   { 
       if (0==pNodeBegin) 
      { 
         pNodeBegin= new TNode; 
         pNodeBegin->pPrev=0; 
         pNodeBegin->pNext=0; 
         pNodeCur=pNodeBegin; 
      } 
       else  if (0!=pNodeCur->pNext) // 还有一个空余的Node 
      { 
         pNodeCur=pNodeCur->pNext; 
      } 
       else 
      { 
         TNode* pNodeEnd= new TNode; 
         pNodeEnd->pPrev=pNodeCur; 
         pNodeEnd->pNext=0; 
         pNodeCur->pNext=pNodeEnd; 
         pNodeCur=pNodeEnd;    
      } 
      ++_size; 
   } 
   ~Tdata_list() 
   { 
       for (TNode* i=pNodeBegin;i!=0; ) 
      { 
         TNode* pNext=i->pNext; 
         delete i; 
         i=pNext; 
      } 
   } 
   unsigned  char* top() 
   { 
          return pNodeCur->Data; 
   } 
    void pop() 
   { 
      TNode*& pTmp=pNodeCur->pNext; 
       if (pTmp!=0) 
      { 
         delete pTmp; // 留一个空余Node,多余的释放 
         pTmp=0; 
      } 
      pNodeCur=pNodeCur->pPrev; 
      --_size; 
   } 
private: 
   TNode*   pNodeCur; 
   TNode*   pNodeBegin; 
   unsigned  int _size; // 用来追踪list的使用大小 
}; 


// myfast_stack 
// 注意它的实现并没有以降低stack的通用能力来提高性能 
// 改进可能:1.提供专署的内存分配器,而不是默认的new/delete; (就可以和SGI中的stack对比测试了)
template< class T, bool IsPOD= false> 
class myfast_stack 
{ 
public: 
    enum{ type_sizes= sizeof(T), // ! 
         node_width=(1020/type_sizes)+1 // 使用这种策略,stack<T,list<T> >也没有存在必要了 
      }; 
    typedef T                           value_type; 
    typedef unsigned  int                  size_type; 
   typedef Tdata_list<type_sizes*node_width>   Tbase_alloc; 

    myfast_stack():_node_begin(0),_node_cur(0),_node_last(0) {} 
    ~myfast_stack() 
   { 
       if ((!IsPOD)&&(_NodeList.size()>0)) 
      { 
          for (T* i=_node_begin;i<_node_cur;++i) 
            i->T::~T(); 
          int nsize=( int)_NodeList.size(); 
          for ( int j=0;j<(nsize-1);++j) 
         { 
            _NodeList.pop(); 
            _node_cur=(T*)_NodeList.top(); 
             for ( int i=0;i<node_width;++i) 
            { 
               _node_cur->T::~T(); 
               ++_node_cur; 
            } 
         } 
      } 
   } 
     bool empty()  const    
      {  return (_node_last==_node_begin)&&(_NodeList.size()<=1); } 
    size_type size()  const 
      {  return (_node_last-_node_begin)+node_width*(max(( int)_NodeList.size()-1,0)); } 
    value_type& top() 
      {  return *(_node_cur-1); } 
     const value_type& top()  const 
      {  return *(_node_cur-1); } 
     void push( const value_type& x) 
   { 
       if (_node_cur==_node_last) 
      { 
         _ToNextNode(); 
      } 
       // std::_Construct(_node_cur,x); 
       new (_node_cur) T(x) ; 
      ++_node_cur; 
   } 
     void pop() 
   { 
       if (_node_cur==_node_begin) 
      { 
         _ToPrevNode(); 
      } 
      --_node_cur; 
      _node_cur->T::~T(); 
   } 
protected: 
   T*            _node_begin; 
   T*            _node_cur; 
   T*            _node_last; 
   Tbase_alloc      _NodeList; 
    void _ToNextNode() 
   { 
      _NodeList.push();    
      _node_begin=(T*)(_NodeList.top()); 
      _node_last=_node_begin+node_width; 
      _node_cur=_node_begin; 
   } 
    void _ToPrevNode() 
   { 
      _NodeList.pop(); 
      _node_begin=(T*)(_NodeList.top()); 
      _node_last=_node_begin+node_width; 
      _node_cur=_node_last; 
   } 
};
C++ STL标准模板库的容器使用大纲 一、std::array(封装固定大小数组的容器) 二、std::deque (双端队列) 三、std::forw 四、std::list 五、std::map 六、std::multimap 七、std::queue 八、std::stack 九、std::vector 十、std::set 阅读详情

相关推荐

C++STL剖析(四)—— stack和queue的概念和使用

stack、queue的介绍和使用

Edison's 小宇宙 2万+

栈---->动态实现

动态栈 栈可以使用静态数组来模拟实现,但是使用静态数组时,栈的大小就必须是固定的,为了弥补这种局限性,栈可以使用动态存储空间,因为在使用过程中可移动台的调整大小; typedef struct{ int key; }element; element* stack; MALLOC(stack,sizeof(stack)); 动态存储空间的扩容问题,一般来说当栈满之后,直接执行的是加倍策略; v

bug_maker 532

记录LeNet的硬件加速器设计(一)

LeNet的硬件加速器设计实现。

2301_81029398的博客 3543

【C++】STL之栈(stack)介绍

栈(stack) 栈是一种运算受限的线性表。限定仅在表尾进行插入和删除的操作。 插入(push), 弹出(pop)。其特性就是先进后出,即先插入的元素最后才能弹出。大家可以把栈想象成一个弹夹,你只能在顶层一颗一颗装入子弹,先装的子弹在最底层,打出时也只能从上层一颗一颗打出。最后装入的一颗子弹最先打出。这便是先进后出的意思。在栈中,“弹夹”的最上层被叫做栈顶,一般用top来指示栈顶的位置 ...

m0_62953149的博客 1万+

用GraphPad Prism直出单细胞亚群堆叠柱状图

用GraphPad Prism直出单细胞亚群堆叠柱状图

fivehuge的博客 4195

WINDOWS下SIP拨号的实现

在WIN10上拨打SIP电话  由于我的交换原理实验有一个是关于SIP的抓包的。而之前从未接触过SIP,所以在如何完成一次SIP拨号上遇到了不少问题,查了好多资料都没有完整的或者写的很清楚的,这里就对那些资料做一个总结,给出一个比较完善的解决方案,一来是记录此次实验的收获,而来也可能为其他有同样困惑的人解惑。  废话不多说直接开始。首先是下载一个可以拨打SIP电话的客户终端,这里我选择的是MicroSIP, 下载红圈内的文件,下载好之后直接一路安装即可,没有什么特别注意的地方。  安装好客户端之后,就需要注

weixin_43918519的博客 3807

c++STL中stack,queue使用方法和模拟实现,及适配器模式编程

1.1 stack的介绍栈是计算机中一个重要的数据结构,因其先进先出的特性,可以解决许多结构上的问题,进行栈的学习,了解它的底层是很有必要的。1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。

qq_58761784的博客 1632

Cortex-M TrustZone实战:从硬件隔离到安全交互的嵌入式开发指南

在嵌入式系统开发中,硬件安全隔离是构建可信执行环境(TEE)的核心技术。其原理是通过处理器硬件机制,将系统资源划分为安全与非安全域,实现内存、外设和代码的强制访问控制。这项技术的核心价值在于,它能在单一芯片上为资源受限的物联网(IoT)设备、智能传感器等提供芯片级的安全基础,无需依赖外置安全元件,从而降低BOM成本并简化设计。典型的应用场景包括保护设备密钥、实现安全启动、构建安全服务(如加密、密钥管理)以供应用程序调用。本文聚焦于Arm Cortex-M系列微控制器上的TrustZone技术实现,针对开发中

weixin_42639246的博客 300

STL: 容器适配器stack 与 queue

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

qq_72916130的博客 2289

手把手教你学Simulink——基于通信协议与标准的场景实例:卫星通信中的网络安全协议(IPSec)仿真

随着卫星通信在国防、金融、应急指挥、能源管网等关键领域的广泛应用,其安全性已成为系统设计的核心要求。然而,卫星链路具有广播特性、长距离传输、地面站分布广等特点,极易遭受窃听、篡改、重放、中间人攻击等网络威胁。传统的“信任链路层安全”已不足以应对现代网络攻击。为此,国际标准组织(如IETF、ETSI、CCSDS)推荐在卫星通信中部署协议,实现端到端的数据机密性、完整性、身份认证和抗重放保护。本文将手把手教你如何在中构建一个符合标准的仿真系统,涵盖从明文传输到加密隧道的完整安全链路建模。本文完整实现了。

MHD0815的博客 148

SI案例分享--考虑ESD保护二极管的差分线阻抗仿真

PCB设计中的cut out参考面早已是SI评估中常用的阻抗优化方式,通过合理的仿真建模,可以提供合适的设计参考,并保持与测试结果的高度一致性。

一只豌豆象的博客 1436

STL-stack的模拟实现

本文内容讲了stack 的模拟实现,更重要是我们初步接触了STL六大组件之一的适配器,知道了STL泛型的强大之处。

fengjunziya的博客 235

【数字IC验证快速入门】26、SystemVerilog项目实践之AHB-SRAMC(6)(AHB-SRAMC SVTB Overview)

导读:作者有幸在中国电子信息领域的排头兵院校“电子科技大学”攻读研究生期间,接触到前沿的数字IC验证知识,旁听到诸如华为海思、清华紫光、联发科技等业界顶尖集成电路相关企业面授课程,对数字IC验证有了一些知识积累和学习心得。为帮助想入门前端IC验证的朋友,思忱一二后,特开此专栏,以期花最短的时间,走最少的弯路,学最多的IC验证技术知识。 文章目录一、内容概述二、AHB-SRAMC 验证计划 一、内容概述 AHB-SRAMC 验证计划 AHB-SRAMC 验证平台的架构图 AHB-SRAMC 验证平台的代.

悟已往之不谏 知来者之可追 2650

STL-stack和queue

stack- queue-priority_queue

qq_62223120的博客 1006

AB测试面试题

AB实验流程以一个实际的业务场景去描述AB实验流程,限定场景来减少和面试官的信息差,确保沟通高效可以以自己简历上某个相关项目的场景来举例,或者以面试的岗位对应的业务来举例子。不能光自己说,要让面试官参与进来,引导面试官往自己熟悉的内容提问,从被动转为主动。回答要有逻辑性,简洁、准确的表达。还是延用之前提到的项目案例,假设之前抖音拍摄页面中右下角相册功能的icon 由相册图标改为 用户相册的最新一张照片这个案例写在了我的简历中。

qq_45727498的博客 857

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

STL 容器 stack 的使用详解

stack容器 stack是堆栈容器,是一种“先进后出”的容器。 #include <stack> stack是基于deque容器而实现的容器 stack对象的默认构造 stack采用模板类实现, stack对象的默认构造形式: stack stkT; stack stkInt; //一个存放int的stack容器。 stack stkFloat; ...

weifc-wei 的博客 535

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

【C++ STL】容器适配器 Container adapter(stack & queue & priority_queue)

文章目录一、STL - stack1.1 stack 的介绍1.2 stack 的使用1.3 stack 的模拟实现二、STL - queue 的介绍2.1 queue 的介绍2.2 queue 的使用2.3 queue 的模拟实现三、STL - priority_queue3.1 priority_queue 的介绍3.2 priority_queue 的使用3.3 仿函数(⭐重要)① 什么是仿函数② 模板实例化时,仿函数的使用3.4 priority_queue 的模拟实现四、容器适配器4.1 什么是容

codewinter的博客 1541

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

【C++】适配器模式手搓STL的stack和queue

适配器模式手搓STL的stack和queue一篇文章就够了!!!

有梦想的电信狗 1361

10.STL中stack和queue的基本使用(附习题)

快一起来学习stack和queue吧

No Game No Life 2380

STL源码剖析 stack 栈 概述->(使用deque双端队列 / list链表)作为stack的底层容器

Stack是一种先进后出的数据结构,他只有一个出口 stack允许 新增元素、移除元素、取得最顶端的元素,但是无法获得stack的内部数据,因此satck没有遍历行为 Stack定义的完整列表 (双端队列作为Stack的底层容器) 将deque作为Stack的底部结构,对其原有的接口进行适配,使其满足"先进后出"的特性 deque是双向开口的数据结构,只需要封闭deque的头端开口(缺省实现),便轻而易举的形成了一个stack。 Stack基于deque,这种“修改某物的接口 形成另外一种事物.

CHYabc123456hh的博客 945

STL——stack和queue

仿函数就是使用起来类似于使用一个函数,但是它实际上不是调用函数,而是通过类的对象调用类中重载的括号运算符成员函数,从而达到我们的目的。仿函数可以作为模板参数使用,因为每个仿函数都拥有自己的类型。仿函数比一般函数更灵活。

weixin_50601177的博客 274

C++:stl:stack、queue、priority_queue介绍及模拟实现和容量适配器deque介绍

本文主要介绍c++中stl的栈、队列和优先级队列并对其模拟实现,对deque进行一定介绍并在栈和队列的模拟实现中使用。

Bottle2023的博客 468
上一篇: 内存对齐的规则以及作用
下一篇: 迭代器之入门介绍(下)
laohan_
博客等级 码龄15年 112粉丝 224原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值