• <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++內(nèi)存分配優(yōu)先使用內(nèi)存池,而不是newdelete

            轉(zhuǎn)載

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

             

            認識一下newdelete的開銷:

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

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

            內(nèi)存池模式:

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

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

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

            c++內(nèi)存池模式的發(fā)展:

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

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

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

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

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

            new,delete調(diào)用與內(nèi)存池調(diào)用的效率對比:

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

            #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()

            {

                // 使用了一些不規(guī)則的分配與釋放,增加內(nèi)存管理的負擔(dān)

                // 但總體流程還是很規(guī)則的,基本上不產(chǎn)生內(nèi)存碎片,要不然反差效果會更大。

             

                allocator<CCC> c_allc;

                allocator<SSS> s_allc;

                allocator<DDD> d_allc;

             

                double re = 0; // 隨便作一些運算,仿止編譯器優(yōu)化掉內(nèi)存分配的代碼

             

                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;

             

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

                time_t begin = time(0);

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

                time_t seporator = time(0);

             

               

                // 運行到系統(tǒng)調(diào)用測試時,感覺機器明顯變慢,

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

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

                re2 = test_allocator<new_delete_alloc>();           // 直接系統(tǒng)調(diào)用

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

             

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

            }

            總結(jié):

            在一個100000000次的循環(huán)中,使用內(nèi)存池是3秒,使用系統(tǒng)調(diào)用是93秒。

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

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

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

            當(dāng)然,有些情況是不可說用什么就用什么的,但如果可選的話,優(yōu)先使用棧上的對象,其次考慮內(nèi)存池,然后再考慮系統(tǒng)調(diào)用。



            posted on 2009-08-26 10:46 李陽 閱讀(1178) 評論(0)  編輯 收藏 引用 所屬分類: C++
            伊人久久大香线蕉综合Av | 亚洲va久久久噜噜噜久久狠狠 | 综合久久久久久中文字幕亚洲国产国产综合一区首 | 亚洲&#228;v永久无码精品天堂久久| 99久久www免费人成精品| 久久国产精品免费一区| 国内精品久久久久影院老司| 久久久精品国产sm调教网站| 久久国产成人午夜AV影院| 2021久久国自产拍精品| 无码任你躁久久久久久| 久久香蕉综合色一综合色88| 久久精品久久久久观看99水蜜桃| 一本伊大人香蕉久久网手机| 无码人妻少妇久久中文字幕蜜桃| 欧美色综合久久久久久| 7国产欧美日韩综合天堂中文久久久久 | www久久久天天com| 亚洲精品乱码久久久久久蜜桃不卡 | 久久久久国产一区二区| 国产韩国精品一区二区三区久久 | 久久精品国产亚洲av日韩| 亚洲国产成人久久精品99| 久久中文精品无码中文字幕| 99久久精品免费国产大片| 精品久久久久久久久中文字幕| 久久狠狠爱亚洲综合影院| 亚洲国产成人精品女人久久久 | 国产成人综合久久综合| 久久综合噜噜激激的五月天| 中文字幕久久波多野结衣av| 久久久久亚洲av综合波多野结衣| 亚洲精品综合久久| 久久久亚洲精品蜜桃臀| 亚洲精品高清一二区久久| 亚洲精品国精品久久99热| 久久久久久精品久久久久| 久久精品国产免费观看| 国内精品久久久久伊人av| 夜夜亚洲天天久久| 武侠古典久久婷婷狼人伊人|