• <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>
            隨筆-4  評論-40  文章-117  trackbacks-0

            c++內存分配優先使用內存池,而不是newdelete

            轉載

            原文出處:http://www.devdiv.net/home/space.php?uid=125&do=blog&id=364

             

            認識一下newdelete的開銷:

            newdelete首先會轉調用到mallocfree,這個大家應該很熟識了。很多人認為malloc是一個很簡單的操作,其實巨復雜,它會執行一個系統調用,從用戶態轉到內核態,該系統調用會鎖住內存硬件,然后通過鏈表的方式查找空閑內存,如果找到大小合適的,就把用戶的進程地址映射到內存硬件地址中,然后釋放鎖,返回用戶態。delete是一個反過程。

            相對的,如果不是使用堆分配,而是直接在棧上分配,比如類型int,那么開銷就是把sp這個寄存器加上sizeof(int)

            內存池模式:

                   內存池就是預先分配好,放到進程空間的內存塊,用戶申請與釋放內存其實都是在進程內進行,SGI-STLalloc遇到小對象時就是基于內存池的。只有當內存池空間不夠時,才會再從系統找一塊很大的內存。

                   內存池模式是如此之重要,以至于讓我想不明白為什么四人幫那本《設計模式》沒有把內存池列為基本模式,目前其它的教材,包括學院教材,實踐教材都沒有列出這個模式(講線程池模式的教材倒非常多)。可能他們認為這不屬于設計,而屬于具體實現吧。但我覺得這樣的后果是間接把很多c++ fans帶向低效的編碼方式。

            sun公司就挺喜歡搞一些算法,用c++實現與java實現一遍,結果顯示c++的效率有時甚至比java低,很多c++高手看了之后都會覺得很難解,其實有玄機:javanew其實是基于內存池的,而c++new是直接系統調用。

            c++內存池模式的發展:

                   c++98標準之前,基本上大多數程序員沒用使用內存池,c++98 標準之后,內存池的使用也只是停留在STL內部的使用上,并沒有得到推廣。

                   其實我認為,STL的內存分配模式是一場變革,它不但包含內存分配的革命,也包含了內存管理(這個話題先放一邊)的革命,只是這場變革被很多人忽略了。也有一些人認為STL的內存分配方案有潛在問題,就是只管從系統分配,但卻永遠不會調用系統級的釋放,如果使用不當,程序拿住的內存會越來越多。我自己工作過的項目沒遇上過這樣的問題,但之前營帳報表組的一個容災項目倒是遇上了。不過STL的內存模式沒有推廣最大的原因還是因為alloc不是標準組件,以至于被人忽略了。

                   STL之后,一些c++ fans們開始搞出了幾套內部使用的內存池。為了項目需要,我自己也曾經做過一個。但這些都沒有很正式的公開,而且也不完美。

                   大概在200x(-_-!),主導c++標準的一群牛人發起了一個叫boost的項目,才正式的把內存池帶到實用與標準化階段。

            插入一點題外話:關于boost,很多人(包括我自己也曾經)產生誤解,認為它是準標準庫,是下一代標準庫。其實boost是套基礎建設,用來證明哪些方案是可行,哪些是不可行的,它里面的一些組件有可能會出局,也有可能不是以庫的方式存在,而是以語言核心的方式存在,下一代標準庫名字叫TR1,再一下代叫TR2(我對使用TR這個名字很費解,為什么不統一叫STL)

            new,delete調用與內存池調用的效率對比:

            講了這么多費話,要到關鍵時候了,用事例來證明為什么要優先使用內存池。下面這段代碼是我很久以前的一段測試案例,細節上可能有點懂難,但流程還是清晰的:

            #include <time.h>

            #include <boost/pool/object_pool.hpp>

             

            struct CCC

            {

                CCC() {}

                char data[10];

            };

             

            struct SSS

            {

                SSS() {}

                short data[10];

            };

             

            struct DDD

            {

                DDD() {}

                double data[10];

            };

             

            // new,delete封裝為一個與boost::object_pool一樣的接口,以便于測試

            template <typename element_type, typename user_allocator = boost::default_user_allocator_malloc_free>

            class new_delete_alloc

            {

            public:

                element_type* construct() { return new element_type; }

                void destroy(element_type* const chunk) { delete chunk; }

            };

             

            template

                template<typename, typename>

                class allocator

            double test_allocator()

            {

                // 使用了一些不規則的分配與釋放,增加內存管理的負擔

                // 但總體流程還是很規則的,基本上不產生內存碎片,要不然反差效果會更大。

             

                allocator<CCC> c_allc;

                allocator<SSS> s_allc;

                allocator<DDD> d_allc;

             

                double re = 0; // 隨便作一些運算,仿止編譯器優化掉內存分配的代碼

             

                for (unsigned int i = 0; i < 10000; ++i)

                {

                    for (unsigned int j = 0; j < 10000; ++j)

                    {

                        CCC* pc = c_allc.construct();

                        SSS* ps = s_allc.construct();

             

                        re += pc->data[2];

                        c_allc.destroy(pc);

             

                        DDD* pd = d_allc.construct();

             

                        re += ps->data[2];

                        re += pd->data[2];

                        s_allc.destroy(ps);

                        d_allc.destroy(pd);

                    }

                }

             

                return re;

            }

             

            int main(int argc, char* argv[])

            {

                double re1 = 0;

                double re2 = 0;

             

                // 運行內存池測試時,基本上對我機器其它進程沒什么影響

                time_t begin = time(0);

                re1 = test_allocator<boost::object_pool>(); // 使用內存池boost::object_pool

                time_t seporator = time(0);

             

               

                // 運行到系統調用測試時,感覺機器明顯變慢,

                // 如果再加上內存碎片的考慮,對其它進程的影響會更大。

                std::cout << long(seporator - begin) << std::endl;

                re2 = test_allocator<new_delete_alloc>();           // 直接系統調用

                std::cout << long(time(0) - seporator) << std::endl;

             

                std::cout << re1 << re2 << std::endl;

            }

            總結:

            在一個100000000次的循環中,使用內存池是3秒,使用系統調用是93秒。

            可能會有人覺得100000000這個數很大,93秒沒什么,但想一下,一個表有幾千萬行是很正常的,如果每行有十多列,每列有數據類型,數據長度,數據內容。如果在這樣的一個循環錯誤的使用了newdelete

            而且以上測試還沒有考慮到碎片的影響,以及運行該程序時對其它程序的影響。而且還有一點,就是機器的內存硬件容量越大,內存分配時,需要搜索的時間就可能越長,如果內存是多條共同工作的,影響就再進一步。

            什么算是錯誤的使用呢,比如返回一個std::string給用戶,有人覺得new出來返回指針給用戶會更好,你可能會想到如果new的話,只產生一次string的構造,如果直接返回對象可能需要多次構造,所以new效率更高。但事實不是這樣,雖然在構造里會有字符串的分配,但其實這個分配是在內存池中進行的,而你直接的那個new就肯定是系統調用。

            當然,有些情況是不可說用什么就用什么的,但如果可選的話,優先使用棧上的對象,其次考慮內存池,然后再考慮系統調用。



            posted on 2009-08-26 10:46 李陽 閱讀(1165) 評論(0)  編輯 收藏 引用 所屬分類: C++
            久久精品国产影库免费看| 久久综合久久美利坚合众国| 97久久超碰国产精品旧版| 久久精品无码午夜福利理论片| 久久久久99精品成人片直播| 国产日韩欧美久久| 久久久噜噜噜久久中文字幕色伊伊| 97久久久久人妻精品专区 | 久久国产精品国产自线拍免费 | 久久99热这里只频精品6| 久久99国内精品自在现线| 久久久精品久久久久特色影视| 天天躁日日躁狠狠久久| 国产L精品国产亚洲区久久| 久久国产欧美日韩精品| 久久精品亚洲男人的天堂| 国产精品久久午夜夜伦鲁鲁| 欧美色综合久久久久久| 久久99国产精品久久| 亚洲国产精品无码久久一线| 日本久久久久久久久久| 久久免费视频观看| 99久久人妻无码精品系列| 久久国产劲爆AV内射—百度| 免费精品久久久久久中文字幕| 久久综合欧美成人| 久久久久国产一级毛片高清版| 国内精品久久久久影院优| 亚洲中文字幕无码久久2020| 色播久久人人爽人人爽人人片AV| 久久se精品一区二区影院| 久久天堂电影网| 2022年国产精品久久久久| avtt天堂网久久精品| 国产精品久久国产精麻豆99网站| 蜜臀av性久久久久蜜臀aⅴ | 国产激情久久久久影院小草 | 97超级碰碰碰碰久久久久| 国产精品99精品久久免费| 久久99久久99小草精品免视看| 国产精品99久久免费观看|