• <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>

            小明思考

            高性能服務(wù)器端計(jì)算
            posts - 70, comments - 428, trackbacks - 0, articles - 0
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            leveldb(http://code.google.com/p/leveldb) 是Google一款基于key-value的數(shù)據(jù)庫,其作者是google大牛jeff dean的作品,功力深厚,有很多細(xì)節(jié)的處理值得我們?nèi)W(xué)習(xí)。

            背景
            我們都知道,對于一個高性能的服務(wù)器端程序來說,內(nèi)存的使用非常重要。C++提供了new/delete來管理內(nèi)存的申請和釋放,但是對于小對象來說,直接使用new/delete代價(jià)比較大,要付出額外的空間和時間,性價(jià)比不高。
            另外,我們也要避免多次的申請和釋放引起的內(nèi)存碎片。一旦碎片到達(dá)一定程度,即使剩余內(nèi)存總量夠用,但由于缺乏足夠的連續(xù)空閑空間,導(dǎo)致內(nèi)存不夠用的假象。
            c++ STL為了避免內(nèi)存碎片,實(shí)現(xiàn)一個復(fù)雜的內(nèi)存池,leveldb中則沒有那么復(fù)雜,只是實(shí)現(xiàn)了一個"一次性"內(nèi)存池Arena。
            在leveldb里面,并不是所有的地方都使用了這個內(nèi)存池,主要是memtable使用,主要是用于臨時存放用戶的更新數(shù)據(jù),由于更新的數(shù)據(jù)可能很小,所以這里使用內(nèi)存池就很合適。

            原理
            為了避免小對象的頻繁分配,需要減少對new的調(diào)用,最簡單的做法就是申請大塊的內(nèi)存,多次分給客戶。
            leveldb用一個vector<char *>來保存所有的內(nèi)存分配記錄,默認(rèn)每次申請4k的內(nèi)存,記錄下剩余指針和剩余內(nèi)存字節(jié)數(shù),每當(dāng)有新的申請,如果當(dāng)前剩余的字節(jié)能滿足需要,則直接返回給用戶,如果不能,對于超過1k的請求,直接new返回,小于1K的請求,則申請一個新的4k塊,從中分配一部分給用戶。
            但是這樣的一個問題就是當(dāng)前塊剩余的部分就浪費(fèi)了,改進(jìn)的方法,針對每個block都記錄剩余字節(jié),這樣就需要遍歷來查找合適的block,要付出一些性能的代價(jià)。google的做法是浪費(fèi)就浪費(fèi)吧:-)
            至于釋放,需要釋放整個內(nèi)存池來釋放所占內(nèi)存,這個和leveldb的需求有關(guān),memtable不需要釋放單次內(nèi)存,flush到硬盤后整個memtable銷毀。





            具體實(shí)現(xiàn)
            讓我們來看看具體實(shí)現(xiàn)。

            定義:<util/arena.h>
            class Arena {
             
            public:
              Arena();
              
            ~Arena();

              
            //分配內(nèi)存
              char* Allocate(size_t bytes);

              
            //對齊分配
              char* AllocateAligned(size_t bytes);

              
            // 當(dāng)前的內(nèi)存使用量
              size_t MemoryUsage() const {
                
            return blocks_memory_ + blocks_.capacity() * sizeof(char*);
              }

             
            private:
              
            //分配內(nèi)存,不能直接分配
              char* AllocateFallback(size_t bytes);
              
            //新生成一個BLOCK
              char* AllocateNewBlock(size_t block_bytes);

              
            //當(dāng)前free指針
              char* alloc_ptr_;
              
            //當(dāng)前BLOCK剩余字節(jié)大小
              size_t alloc_bytes_remaining_;

              
            //保存所有分配的內(nèi)存
              std::vector<char*> blocks_;

              
            //已經(jīng)分配的總內(nèi)存大小
              size_t blocks_memory_;

              
            //禁止copy構(gòu)造
              Arena(const Arena&);
              
            void operator=(const Arena&);
            };

            實(shí)現(xiàn)<util/arena..cc>

            //默認(rèn)BLOCK size
            static const int kBlockSize = 4096;

            inline 
            char* Arena::Allocate(size_t bytes) {
              
            //如果申請量能滿足需要,直接分配
              if (bytes <= alloc_bytes_remaining_) {
                
            char* result = alloc_ptr_;
                alloc_ptr_ 
            += bytes;
                alloc_bytes_remaining_ 
            -= bytes;
                
            return result;
              }
              
            return AllocateFallback(bytes);
            }

            char* Arena::AllocateFallback(size_t bytes) {
              
            //大于1KB,直接分配一個新的BLOCK
              if (bytes > kBlockSize / 4) {
                
            char* result = AllocateNewBlock(bytes);
                
            return result;
              }

              
            //申請一個新的BLOCK,浪費(fèi)少于當(dāng)前申請bytes的剩余空間 
              alloc_ptr_ = AllocateNewBlock(kBlockSize);
              alloc_bytes_remaining_ 
            = kBlockSize;

              
            //設(shè)置free指針和剩余大小
              char* result = alloc_ptr_;
              alloc_ptr_ 
            += bytes;
              alloc_bytes_remaining_ 
            -= bytes;
              
            return result;
            }

            其他
            對于那些不使用arena來分配的內(nèi)存,怎么去優(yōu)化呢?leveldb使用tcmalloc進(jìn)行優(yōu)化。tcmalloc是google-perftool中的一個工具,用于替代glibc的默認(rèn)new實(shí)現(xiàn)。
            看看leveldb的Makefile就知道,使用tcmalloc很簡單,只需要加入-ltcmalloc重新編譯就可以了。

            # If Google Perf Tools are installed, add compilation and linker flags
            # (see http:
            //code.google.com/p/google-perftools/)
            ifeq ($(GOOGLE_PERFTOOLS), 1)
            GOOGLE_PERFTOOLS_LDFLAGS
            =-ltcmalloc
            else
            GOOGLE_PERFTOOLS_LDFLAGS
            =
            endif

            順便提一下,redis使用的是jemalloc來提高malloc的效率

            這里有一個性能比較,看起來tcmalloc的性能最好。

            Allocator   CPU Time (min)  Commit Memory  Region Support  
            MSVC malloc       2:59          543 MB              No
            ptmalloc      2:01          480 MB         Yes
            ned malloc        2:01          652 MB         Yes
            tc malloc         1:39          454 MB         No
            je malloc     1:59          496 MB         No

            請參考:

            亚洲va久久久噜噜噜久久男同| 亚洲国产小视频精品久久久三级| 久久久久久国产a免费观看不卡 | 91久久精品视频| 精品永久久福利一区二区| 亚洲精品无码久久久久去q| 狠狠色丁香久久婷婷综合_中| 欧美日韩精品久久久久| 欧美性大战久久久久久| 综合久久一区二区三区| 99久久免费国产精品特黄| 久久久久久久91精品免费观看| 一级女性全黄久久生活片免费 | 久久久久久久综合日本亚洲 | 久久无码AV一区二区三区| 亚洲精品成人久久久| 亚洲精品综合久久| 亚洲精品乱码久久久久久 | 久久久久久国产精品美女| 久久婷婷五月综合成人D啪| 亚洲欧美一区二区三区久久| 欧美亚洲国产精品久久| 亚洲精品无码成人片久久| 国产精品一区二区久久| 久久精品国产国产精品四凭 | 国产成人久久精品激情| 久久最近最新中文字幕大全| 久久久精品日本一区二区三区| 亚洲第一永久AV网站久久精品男人的天堂AV | 国产麻豆精品久久一二三| 国产真实乱对白精彩久久| 久久精品日日躁夜夜躁欧美| 国产精品99久久精品| 亚洲伊人久久综合影院| 久久精品9988| 久久久久se色偷偷亚洲精品av | 一本色道久久88精品综合| 99久久99久久精品国产片果冻| 久久精品国产99国产精品导航| 国产精品成人精品久久久| 久久夜色精品国产噜噜麻豆|