• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            SGI STL中默認(rèn)Allocator為何變?yōu)閚ew_allocator?

            peakflys原創(chuàng)作品,轉(zhuǎn)載請(qǐng)保留原作者和源鏈接
               項(xiàng)目中和自己代碼中大量使用了STL的容器,平時(shí)也沒怎么關(guān)注alloc的具體實(shí)現(xiàn)細(xì)節(jié),主觀認(rèn)識(shí)上還停留在侯捷大師的《STL源碼剖析》中的講解。
               以下為書中摘錄截圖:詳見書中2.2.4節(jié)內(nèi)容

            前段時(shí)間項(xiàng)目中出了一個(gè)內(nèi)存問題,在追查問題的過程中查看了對(duì)應(yīng)的源碼(版本為libstdc++-devel-4.1.2)
            源碼文件c++allocator.h中定義了默認(rèn)的Alloc:#ifndef _CXX_ALLOCATOR_H
            #define _CXX_ALLOCATOR_H 1

            // Define new_allocator as the base class to std::allocator.
            #include <ext/new_allocator.h>
            #define __glibcxx_base_allocator  __gnu_cxx::new_allocator

            #endif
            查看new_allocator.h文件,發(fā)現(xiàn)new_allocator僅僅是對(duì)operator new和operator delete的簡(jiǎn)單封裝(感興趣的朋友可自行查看)。
            眾所周知libstdc++中STL的大部分實(shí)現(xiàn)是取自SGI的STL,而《STL源碼剖析》的源碼是Cygnus C++ 2.91則是SGI STL的早期版本,下載源碼看了一下allocator的實(shí)現(xiàn)確實(shí)如書中所言。
            不知道從哪個(gè)版本起,SGI的STL把默認(rèn)的Alloc替換成了new_allocator,有興趣的同學(xué)可以查一下。
            知道結(jié)果后,可能很多人和我一樣都不禁要問:Why?
            以下是兩個(gè)版本的源碼實(shí)現(xiàn):
            1、new_allocator
                  // NB: __n is permitted to be 0.  The C++ standard says nothing
                  
            // about what the return value is when __n == 0.
                  pointer
                  allocate(size_type __n, const void* = 0)
                  {
                if (__builtin_expect(__n > this->max_size(), false))
                  std::__throw_bad_alloc();

                return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
                  }

                  // __p is not permitted to be a null pointer.
                  void
                  deallocate(pointer __p, size_type)
                  { ::operator delete(__p); }
            2、__pool_alloc
              template<typename _Tp>
                _Tp*
                __pool_alloc<_Tp>::allocate(size_type __n, const void*)
                {
                  pointer __ret = 0;
                  if (__builtin_expect(__n != 0, true))
                {
                  if (__builtin_expect(__n > this->max_size(), false))
                    std::__throw_bad_alloc();

                  // If there is a race through here, assume answer from getenv
                  
            // will resolve in same direction.  Inspired by techniques
                  
            // to efficiently support threading found in basic_string.h.
                  if (_S_force_new == 0)
                    {
                      if (getenv("GLIBCXX_FORCE_NEW"))
                    __atomic_add(&_S_force_new, 1);
                      else
                    __atomic_add(&_S_force_new, -1);
                    }

                  const size_t __bytes = __n * sizeof(_Tp);
                  if (__bytes > size_t(_S_max_bytes) || _S_force_new == 1)
                    __ret = static_cast<_Tp*>(::operator new(__bytes));
                  else
                    {
                      _Obj* volatile* __free_list = _M_get_free_list(__bytes);

                      lock sentry(_M_get_mutex());
                      _Obj* __restrict__ __result = *__free_list;
                      if (__builtin_expect(__result == 0, 0))
                    __ret = static_cast<_Tp*>(_M_refill(_M_round_up(__bytes)));
                      else
                    {
                      *__free_list = __result->_M_free_list_link;
                      __ret = reinterpret_cast<_Tp*>(__result);
                    }
                      if (__builtin_expect(__ret == 0, 0))
                    std::__throw_bad_alloc();
                    }
                }
                  return __ret;
                }
              template<typename _Tp>
                void
                __pool_alloc<_Tp>::deallocate(pointer __p, size_type __n)
                {
                  if (__builtin_expect(__n != 0 && __p != 0, true))
                {
                  const size_t __bytes = __n * sizeof(_Tp);
                  if (__bytes > static_cast<size_t>(_S_max_bytes) || _S_force_new == 1)
                    ::operator delete(__p);
                  else
                    {
                      _Obj* volatile* __free_list = _M_get_free_list(__bytes);
                      _Obj* __q = reinterpret_cast<_Obj*>(__p);

                      lock sentry(_M_get_mutex());
                      __q ->_M_free_list_link = *__free_list;
                      *__free_list = __q;
                    }
                }
                }
            從源碼中可以看出new_allocator基本就沒有什么實(shí)現(xiàn),僅僅是對(duì)operator new和operator delete的封裝,而__pool_alloc的實(shí)現(xiàn)基本和《STL源碼剖析》中一樣,所不同的是加入了多線程的支持和強(qiáng)制operator new的判斷。
            無論從源碼來看,還是實(shí)際的測(cè)試(后續(xù)會(huì)附上我的測(cè)試版本),都可以看出__pool_alloc比new_allocator更勝一籌。

            同很多人討論都不得其解,網(wǎng)上也很少有關(guān)注這個(gè)問題的文章和討論,倒是libstdc++的官網(wǎng)文檔有這么一段:
            (peakflys注:文檔地址:https://gcc.gnu.org/onlinedocs/libstdc++/manual/memory.html#allocator.default)
            從文檔的意思來看,選擇new_allocator是基于大量測(cè)試,不幸的是文檔中鏈接的測(cè)試?yán)泳鶡o法訪問到……不過既然他們說基于測(cè)試得出的結(jié)果,我就隨手寫了一個(gè)自己的例子:
            #include <map>
            #include <vector>
            #ifdef _POOL
            #include <ext/pool_allocator.h>
            #endif

            static const unsigned int Count = 1000000;

            using namespace std;

            struct Data
            {
                int a;
                double b;
            };

            int main()
            {
            #ifdef _POOL
                map<int, Data, less<int>, __gnu_cxx::__pool_alloc<pair<int, Data> > > mi;
                vector<Data, __gnu_cxx::__pool_alloc<Data> > vi;
            #else
                map<int, Data> mi;
                vector<Data> vi;
            #endif

                for(int i = 0; i < Count; ++i)
                {
                    Data d;
                    d.a = i;
                    d.b = i * i;
                    mi[i] = d;
                    vi.push_back(d);
                }
                mi.clear();
            #ifdef _POOL
                vector<Data, __gnu_cxx::__pool_alloc<Data> >().swap(vi);
            #else
                vector<Data>().swap(vi);
            #endif
                for(int i = 0; i < Count; ++i)
                {
                    Data d;
                    d.a = i;
                    d.b = i * i;
                    mi[i] = d;
                    vi.push_back(d);
                }
                return 0;
            }
            因?yàn)楫?dāng)數(shù)據(jù)大于128K時(shí),__pool_alloc同new_allocator一樣直接調(diào)用operator,所以例子中構(gòu)造出的Data小于128K,來模擬兩個(gè)分配器的不同。同時(shí)如libstdc++官網(wǎng)中說的,我們同時(shí)使用了sequence容器vector和associate容器map。
            例子中模擬了兩種類型容器的插入-刪除-插入的過程,同時(shí)里面包含了元素的構(gòu)造、析構(gòu)以及內(nèi)存的分配和回收。
            以下是在我本地機(jī)器上運(yùn)行的結(jié)果:
            1、-O0的版本:

            2、-O2的版本:

            多線程的測(cè)試?yán)游揖筒毁N了,測(cè)試結(jié)果大致和上面相同,大家可以自行測(cè)試。
            從多次運(yùn)行的結(jié)果來看__pool_alloc的性能始終是優(yōu)于new_allocator的。
            又回到那個(gè)問題,為什么SGI STL的官方把默認(rèn)的Alloc從__pool_alloc變?yōu)閚ew_allocator。
            本篇文章不能給大家一個(gè)答案,官方網(wǎng)站上也未看到解釋,自己唯一可能的猜測(cè)是
            1、__pool_alloc不利于使用者自定義operator new和operator delete(其實(shí)這條理由又被我自己推翻了,因?yàn)橥ㄟ^源碼可以知道置位_S_force_new即可解決)
            2、malloc性能的提升以及硬件的更新導(dǎo)致使用默認(rèn)的operator new即可。

            如果大家有更好,更權(quán)威的答案請(qǐng)告訴我(peakflys@gmail.com)或者留言,謝謝。
                                                                              by peakflys 16:49:49 Wednesday, January 14, 2015

            posted on 2015-01-14 16:50 peakflys 閱讀(4399) 評(píng)論(8)  編輯 收藏 引用 所屬分類: C++

            評(píng)論

            # re: SGI STL中默認(rèn)Allocator為何變?yōu)閚ew_allocator? 2015-01-15 00:12 egmkang

            跟硬件沒關(guān)系,malloc性能提升.因?yàn)閙alloc本身在thread上面就有一個(gè)pool.
            而且高性能的分配器越來越多,比如tcmalloc/jemalloc,已經(jīng)沒有任何必要再提供一個(gè)pool allocator  回復(fù)  更多評(píng)論   

            # re: SGI STL中默認(rèn)Allocator為何變?yōu)閚ew_allocator? 2015-01-15 09:12 蘋果汁

            樓主好學(xué) 贊一個(gè)  回復(fù)  更多評(píng)論   

            # re: SGI STL中默認(rèn)Allocator為何變?yōu)閚ew_allocator?[未登錄] 2015-01-15 13:01 chipset

            pool不拼接內(nèi)存,一塊大內(nèi)存被分割成很多相等的小塊,此時(shí)調(diào)用分配一塊較大內(nèi)存可能失敗,不是因?yàn)閮?nèi)存不夠用,而是因?yàn)槿欠指畛尚∷閴K不拼接導(dǎo)致的。這是Pool的致命傷。再者Pool的效率也高不到哪里,以不拼接小塊換速度的做法不值得提倡。  回復(fù)  更多評(píng)論   

            # re: SGI STL中默認(rèn)Allocator為何變?yōu)閚ew_allocator? 2015-01-15 17:42 xxoo

            樓上的,內(nèi)存池的作用不就是這個(gè)嗎?你申請(qǐng)一塊內(nèi)存,可以做到物理不連續(xù)?  回復(fù)  更多評(píng)論   

            # re: SGI STL中默認(rèn)Allocator為何變?yōu)閚ew_allocator? 2015-01-15 19:23 peakflys

            如果我沒記錯(cuò)的話malloc自始至終都有自己的一套pool策略,所以我所說的malloc性能提升并非指的這個(gè)。
            不過第三方穩(wěn)定高效的allocator實(shí)現(xiàn)可能是標(biāo)準(zhǔn)庫(kù)作者放棄pool的一個(gè)原因@egmkang
              回復(fù)  更多評(píng)論   

            # re: SGI STL中默認(rèn)Allocator為何變?yōu)閚ew_allocator? 2015-01-15 19:31 peakflys

            說的不錯(cuò),pool的方式存在的問題是挺多。不過對(duì)于pool的效率高不到哪去的觀點(diǎn)我不敢認(rèn)同,針對(duì)大量小數(shù)據(jù)的分配,有時(shí)候還必須得使用pool的方式,不然ptmalloc、tcmalloc、jemalloc等不會(huì)維護(hù)復(fù)雜的內(nèi)存分配方式@chipset  回復(fù)  更多評(píng)論   

            # re: SGI STL中默認(rèn)Allocator為何變?yōu)閚ew_allocator? 2015-01-20 15:24 juegoskizi

            我很喜歡,很不錯(cuò)的職位。  回復(fù)  更多評(píng)論   

            # re: SGI STL中默認(rèn)Allocator為何變?yōu)閚ew_allocator?[未登錄] 2016-03-07 14:09 JAKE

            樓主,有個(gè)問題請(qǐng)教下:
            我有個(gè)應(yīng)用場(chǎng)景,即線程A使用obj_pool分配了一個(gè)對(duì)象obj,線程B用完obj后歸還給pool。 這里肯定會(huì)有線程同步隱患。

            目前我使用兩個(gè)lockfree隊(duì)列完美解決這個(gè)問題,但很繁瑣。

            我想問的是
            使用tcmalloc的話,能解決我上邊所說的問題嗎?  回復(fù)  更多評(píng)論   

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導(dǎo)航

            統(tǒng)計(jì)

            公告

            人不淡定的時(shí)候,就愛表現(xiàn)出來,敲代碼如此,偶爾的靈感亦如此……

            常用鏈接

            留言簿(4)

            隨筆分類

            隨筆檔案

            文章檔案

            搜索

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久伊人五月天论坛| 久久久久久国产精品免费无码| 激情伊人五月天久久综合| 久久大香萑太香蕉av| 久久受www免费人成_看片中文| 日韩欧美亚洲国产精品字幕久久久| 99久久精品九九亚洲精品| 久久综合综合久久97色| 99精品久久久久久久婷婷| 办公室久久精品| 亚洲国产精品无码久久久久久曰 | 久久天天躁狠狠躁夜夜2020| 久久久精品国产Sm最大网站| 狠狠色丁香久久婷婷综合_中| 久久亚洲国产精品成人AV秋霞| 亚洲午夜久久久久久久久电影网| 久久久亚洲欧洲日产国码二区 | 国产成人精品久久一区二区三区av| 久久国产高清字幕中文| 久久综合色区| 日韩AV无码久久一区二区| 色综合久久天天综合| 亚洲一区精品伊人久久伊人| 久久青青草原精品国产| 人人狠狠综合久久亚洲婷婷| 日韩久久无码免费毛片软件| 婷婷综合久久中文字幕蜜桃三电影| 精品久久久久久国产| 亚洲国产天堂久久久久久| 国内精品久久人妻互换| 亚洲国产成人乱码精品女人久久久不卡| 久久一日本道色综合久久| 久久久久亚洲爆乳少妇无 | 久久亚洲精品中文字幕| 久久久久综合中文字幕| 无码AV中文字幕久久专区| 久久国产成人午夜AV影院| 婷婷久久久亚洲欧洲日产国码AV| 亚洲狠狠久久综合一区77777| 伊人久久综合无码成人网| 国产一区二区三精品久久久无广告|