漫步Facebook開源C++庫folly(1):string類的設計

就在近日匠题,F(xiàn)acebook宣布開源了內部使用的C++底層庫拯坟,總稱folly,包括散列韭山、字符串郁季、向量、內存分配、位處理等,以滿足大規(guī)模高性能的需求置森。

這里是folly的github地址:https://github.com/facebook/folly

在folly項目的Overview.md中,談到了folly庫的初衷:

It complements (as opposed to competing against) offerings such as Boost and of course?std. In fact, we embark on defining our own component only when something we need is either not available, or does not meet the needed performance profile.

除了小部分是對現(xiàn)有標準庫和Boost庫功能上的補充塞琼,大部分都是基于性能的需求而“重新制造輪子”。

特別是大規(guī)模下的性能需求禁舷,大規(guī)模下的性能追求是Folly統(tǒng)一的主題:

Good performance at large scale?is a unifying theme in all of Folly.



為什么先談string類彪杉?

一是因為string幾乎是C++程序中最常用的“容器”毅往,性能至關重要;

二是因為之前也曾寫過一篇博客《std::string的Copy-on-Write:不如想象中美好》派近,研究了std::string的copy-on-write實現(xiàn)的優(yōu)缺點攀唯,因此想要看看Facebook究竟需要什么樣的string。


folly自定義的string(以下簡稱為fbstring)的核心實現(xiàn)位于?folly/FBString.h渴丸。

還有一些fbstring的輔助函數(shù)(如向std::string的轉換侯嘀、各種格式的輸出、escape谱轨、demangle等)戒幔,位于?folly/String.h?和?folly/String.cpp?,由于本文主要談的是fbstring的內部實現(xiàn)土童,這些內容暫且不提诗茎,有興趣的童鞋可以自己參考源碼,folly的代碼還是寫得相當漂亮的:)


folly對string類的設計和優(yōu)化献汗,主要體現(xiàn)在兩個方面:

1. 內存模型

2. 常用方法的優(yōu)化

下面將逐一說明敢订。


一. 內存模型

1. 內存布局及策略

fbstring使用了三層的存儲策略(three-tiered storage strategy),根據(jù)長度將fbstring分為三類:small/medium/large罢吃,分別采取不同的優(yōu)化措施楚午,以達到最佳性能。

fbstring內存模型示意圖(使用LucidChart繪制):


簡單來說:

短string:直接放在(棧上)對象中尿招,避免了動態(tài)內存分配的開銷矾柜。結構體長度為24字節(jié),減去末尾的1字節(jié)(用來表示長度)和為結束符'\0'(data()和c_str()方法的需要)預留的1字節(jié)泊业,可以放置22字節(jié)的有效長度把沼。

中等string(小于255字節(jié)):直接通過malloc分配啊易,并且采用eager-copy的方式吁伺,即字符串的復制總是會重新分配并拷貝內容。

至于為什么不用copy-on-write:

1. 我之前的博客也提到租谈,copy-on-write的額外開銷(原子操作篮奄、容易失效)一定程度上抵消了減少一次內存分配和拷貝帶來的好處

2. folly鼓勵使用jemalloc來代替glibc下默認的ptmalloc2,并且在代碼中迎合jemalloc的使用做了大量優(yōu)化割去。在這里窟却,分配一個小片內存區(qū)域的開銷是極小的,下文還會有說明呻逆。

較長string(大于255字節(jié)):使用copy-on-write夸赫,減少分配和拷貝大內存的開銷。在這里咖城,folly使用了C++11中的原子變量:std::atomic來管理引用計數(shù)茬腿,并在引用計數(shù)減為0時銷毀內存呼奢。

PS:使用capacity最高位的4個bits來判斷string的種類,folly假定機器的字節(jié)序為小端(little endian)切平,適用于x86-64平臺上的大部分OS握础。另外順便給大家推薦一個交流學習群:812--855--908


2. 內存分配器

與std::string不同,fbstring并沒有從模板參數(shù)之一的Allocator獲取內存悴品,而是直接使用malloc/free管理內存禀综。


fbstring推薦使用jemalloc而不是Linux下glibc默認的ptmalloc2來管理動態(tài)內存:

1. 作為FreeBSD上的默認分配器,jemalloc在多線程并發(fā)的環(huán)境下表現(xiàn)更好(與google開源的tcmalloc性能相近)苔严。

在tcmalloc的論文《TCMalloc : Thread-Caching Malloc》中定枷,提到了ptmalloc2在多線程環(huán)境下的一個致命缺陷:

ptmalloc2同樣通過為不同的線程分配自己的內存池(Arena)的方式來減少并發(fā)分配時的鎖沖突,但ptmalloc2中線程擁有的內存池是不能遷移的邦蜜,在某些情況下能夠帶來巨大的內存浪費:比如一個線程在開始階段分配了300MB的內存進行初始化工作依鸥,然后釋放了,但接下來的線程分配到不同的內存池悼沈,那么之前的300MB是無法重復利用的贱迟。


2. folly如果檢測到使用jemalloc,那么將使用jemalloc的一些非標準擴展接口來提高性能絮供。

PS:folly通過定義弱符號(weak symbol)的方法來運行時判斷是否使用了jemalloc:

extern"C"intrallocm(void**, size_t*, size_t, size_t,int) __attribute__((weak));/**

* Determine if we are using jemalloc or not.

*/inline bool usingJEMalloc() {

? returnrallocm != NULL;

}

如果使用了jemalloc衣吠,一個典型的優(yōu)化是使用jemalloc特有的rallocm來代替標準的realloc方法。(下面還會提到realloc的優(yōu)化)

同時壤靶,所有動態(tài)內存請求的大小都會經(jīng)過一個過濾函數(shù):goodMallocSize(在folly/Malloc.h中)處理缚俏,以獲取一個對jemalloc友好的值

goodMallocSize在不同的請求區(qū)間,將請求大小設置為64b / 256b / 4KB / 4MB對齊贮乳,以提高分配/回收效率忧换,減少內存碎片。


二. 常見操作的優(yōu)化

fbstring在實現(xiàn)時做了很多優(yōu)化(如word-wise copy等)向拆,其中的細節(jié)不再一一敷述亚茬,感興趣的讀者建議去參考源碼,這里只列出重要的幾點:

1. 末尾'\0'的處理

fbstring的默認行為是“懶惰”添加'\0'(lazy append)浓恳,即平時預留空間刹缝,只在調用data()或者c_str()時,才在結尾添加'\0'颈将,避免了每次修改字符串時的額外開銷(特別是push_back操作)梢夯,因為這樣做是符合C++標準的。

(當然晴圾,fbstring也有相應的宏來關閉該行為)


2. realloc的處理

string很多時候需要realloc颂砸,為了優(yōu)化realloc的效率,fbstring做了這樣的設定:

(1)如果使用jemalloc:使用jemalloc的非標準接口——rallocm

(2)沒有使用jemalloc:

當前內存的使用率小于50%(size * 2 < capacity),放棄使用realloc(因為realloc可能需要拷貝全部內存人乓,而其中超過一半是無效內容)梗醇,而是簡單采用free+malloc+copy的方式來重新分配內存,減少拷貝開銷撒蟀。

當前內存的使用率大于50%叙谨,則使用realloc,寄希望realloc可以合并后面的內存(coalescing)以避免拷貝保屯。


3. 優(yōu)化string::find()

glibc的string::find()實現(xiàn)中只實現(xiàn)了簡單的逐字符查找比較功能手负,復雜度為O(M*N)。(C++標準并沒有規(guī)定string::find的復雜度要求)

find使用了簡化的Boyer-Moore算法姑尺,代碼中聲稱:

Casual tests indicate a?30x?speed improvement overstring::find()for successful searches and a?1.5x?speed improvement for failed searches.

如果是簡單的短字符查詢竟终,string::find()應該足夠高效。只有在長字符搜索的情況下切蟋,find的BM算法實現(xiàn)才能體現(xiàn)出優(yōu)勢统捶,或許這也是Facebook的常用場景吧。


結語:

順便提一下柄粹,fbstring(FBString.h)的作者為Andrei Alexandrescu(熟悉C++應該都聽說過)喘鸟,近距離欣賞大師的代碼實在是一種享受。

同時驻右,Alexandre大叔以43歲的“高齡”什黑,依然在Facebook寫著如此底層的程序。個中滋味堪夭,值得天朝所有浮躁的程序員(包括筆者在內)和“35歲論“者細細體味愕把。

主要有C/C++,Linux森爽,Nginx恨豁,ZeroMQ,MySQL爬迟,Redis橘蜜,fastdfs,MongoDB雕旨,ZK扮匠,流媒體捧请,CDN凡涩,P2P,K8S疹蛉,Docker活箕,TCP/IP,協(xié)程可款,DPDK技術育韩,面試技巧方面的資料技術討論克蚂。

感興趣的朋友戳這里:正在跳轉

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市筋讨,隨后出現(xiàn)的幾起案子埃叭,更是在濱河造成了極大的恐慌,老刑警劉巖悉罕,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赤屋,死亡現(xiàn)場離奇詭異,居然都是意外死亡壁袄,警方通過查閱死者的電腦和手機类早,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗜逻,“玉大人涩僻,你說我怎么就攤上這事≌磺辏” “怎么了逆日?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長萄凤。 經(jīng)常有香客問我屏富,道長,這世上最難降的妖魔是什么蛙卤? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任狠半,我火速辦了婚禮,結果婚禮上颤难,老公的妹妹穿的比我還像新娘神年。我一直安慰自己,他們只是感情好行嗤,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布已日。 她就那樣靜靜地躺著,像睡著了一般栅屏。 火紅的嫁衣襯著肌膚如雪飘千。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天栈雳,我揣著相機與錄音护奈,去河邊找鬼。 笑死哥纫,一個胖子當著我的面吹牛霉旗,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼厌秒,長吁一口氣:“原來是場噩夢啊……” “哼读拆!你這毒婦竟也來了?” 一聲冷哼從身側響起鸵闪,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤檐晕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚌讼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棉姐,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年啦逆,在試婚紗的時候發(fā)現(xiàn)自己被綠了伞矩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡夏志,死狀恐怖乃坤,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情沟蔑,我是刑警寧澤湿诊,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站瘦材,受9級特大地震影響厅须,放射性物質發(fā)生泄漏。R本人自食惡果不足惜食棕,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一朗和、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧簿晓,春花似錦眶拉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谒臼,卻和暖如春朝刊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜈缤。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工拾氓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人劫樟。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓痪枫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叠艳。 傳聞我的和親對象是個殘疾皇子奶陈,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容