《LINUX實操:減少 curl 中內(nèi)存分配操作(malloc)》要點:
本文介紹了LINUX實操:減少 curl 中內(nèi)存分配操作(malloc),希望對您有用。如果有疑問,可以聯(lián)系我們。
本日我在 libcurl 內(nèi)部又做了一個小改動,使其做更少的 malloc.這一次,泛型鏈表函數(shù)被轉(zhuǎn)換成更少的 malloc (這才是鏈表函數(shù)應(yīng)有的方式,真的).
?
幾周前我開始研究內(nèi)存分派.這很容易,因為多年前我們 curl 中就已經(jīng)有內(nèi)存調(diào)試和日志記錄系統(tǒng)了.使用 curl 的調(diào)試版本,并在我的構(gòu)建目錄中運行此腳本:
對于 curl 7.53.1,這年夜約有 115 次內(nèi)存分配.這算多還是少?
內(nèi)存日志異常基礎(chǔ).為了讓你有所了解,這是一個示例片段:
MEM getinfo.c:70 free((nil))
MEM getinfo.c:73 free((nil))
MEM url.c:294 free((nil))
MEM url.c:297 strdup(0x559e7150d616)(24)=0x559e73760f98
MEM url.c:294 free((nil))
MEM url.c:297 strdup(0x559e7150d62e)(22)=0x559e73760fc8
MEM multi.c:302 calloc(1,480)=0x559e73760ff8
MEM hash.c:75 malloc(224)=0x559e737611f8
MEM hash.c:75 malloc(29152)=0x559e737a2bc8
MEM hash.c:75 malloc(3104)=0x559e737a9dc8
?
然后,我對日志進行了更深入的研究,我意識到在相同的代碼行做了許多小內(nèi)存分配.我們顯然有一些相當(dāng)愚蠢的代碼模式,我們分配一個結(jié)構(gòu)體,然后將該結(jié)構(gòu)添加到鏈表或哈希,然后該代碼隨后再添加另一個小結(jié)構(gòu)體,如此這般,而且經(jīng)常在循環(huán)中執(zhí)行.(我在這里說的是我們,不是為了責(zé)怪某個人,當(dāng)然大部分的責(zé)任是我本身……)
這兩種分配操作將總是成對地出現(xiàn),并被同時釋放.我決定辦理這些問題.做非常小的(小于 32 字節(jié))的分配也是浪費的,因為非常多的數(shù)據(jù)將被用于(在 malloc 系統(tǒng)內(nèi))跟蹤那個微小的內(nèi)存區(qū)域.更不用說堆碎片了.
因此,將該哈希和鏈表代碼修復(fù)為不使用 malloc 是快速且簡單的辦法,對于最簡單的 “curl http://localhost” 傳輸,它可以消除 20% 以上的 malloc.
此時,我根據(jù)大小對所有的內(nèi)存分配操作進行排序,并檢查所有最小的分配操作.一個突出的部門是在 curl_multi_wait()
中,它是一個典型的在 curl 傳輸主循環(huán)中被反復(fù)調(diào)用的函數(shù).對于大多數(shù)典型情況,我將其轉(zhuǎn)換為使用堆棧.在大量重復(fù)的調(diào)用函數(shù)中避免 malloc 是一件好事.
?
現(xiàn)在,如上面的腳本所示,同樣的 curl localhost
命令從 curl 7.53.1 的 115 次分配操作下降到 80 個分配操作,而沒有犧牲任何器械.輕松地有 26% 的改善.一點也不差!
由于我修改了 curl_multi_wait()
,我也想看看它實際上是如何改進一些稍微更高級一些的傳輸.我使用了 multi-double.c 示例代碼,添加了初始化內(nèi)存記錄的調(diào)用,讓它使用 curl_multi_wait()
,而且并行下載了這兩個 URL:
http://www.example.com/
http://localhost/512M
第二個文件是 512 兆字節(jié)的零,第一個文件是一個 600 字節(jié)的公共 html 頁面.這是 count-malloc.c 代碼.
首先,我使用 7.53.1 來測試上面的例子,并使用 memanalyze
腳本反省:
Mallocs:33901
Reallocs:5
Callocs:24
Strdups:31
Wcsdups:0
Frees:33956
Allocations:33961
Maximum allocated:160385
好了,所以它總共使用了 160KB 的內(nèi)存,分配操作次數(shù)跨越 33900 次.而它下載跨越 512 兆字節(jié)的數(shù)據(jù),所以它每 15KB 數(shù)據(jù)有一次 malloc.是好是壞?
回到 git master,現(xiàn)在是 7.54.1-DEV 的版本 - 因為我們不太確定當(dāng)我們發(fā)布下一個版本時會變成哪個版本號.它可能是 7.54.1 或 7.55.0,它還尚未確定.我離題了,我再次運行相同修改的 multi-double.c 示例,再次對內(nèi)存日志運行 memanalyze,申報來了:
Mallocs:69
Reallocs:5
Callocs:24
Strdups:31
Wcsdups:0
Frees:124
Allocations:129
Maximum allocated:153247
我不敢置信地反復(fù)看了兩遍.產(chǎn)生什么了嗎?為了仔細(xì)檢查,我最好再運行一次.無論我運行多少次,結(jié)果還是一樣的.
?
在典型的傳輸中 curl_multi_wait()
被調(diào)用了很多次,而且在傳輸過程中至少要正常進行一次內(nèi)存分配操作,因此刪除那個單一的微小分配操作對計數(shù)器有非常大的影響.正常的傳輸也會做一些??數(shù)據(jù)移入或移出鏈表和散列操作,但是它們現(xiàn)在也大都是無 malloc 的.簡單地說:剩余的分配操作不會在傳輸循環(huán)中執(zhí)行,所以它們的重要性不大.
以前的 curl 是當(dāng)前示例分派操作數(shù)量的 263 倍.換句話說:新的是舊的分派操作數(shù)量的 0.37% .
另外還有一點好處,新的內(nèi)存分派量更少,總共減少了 7KB(4.3%).
?
在幾個 G 內(nèi)存的時代里,在傳輸中有幾個 malloc 真的對付普通人有顯著的區(qū)別嗎?對 512MB 數(shù)據(jù)進行的 33832 個額外的 malloc 有什么影響?
為了衡量這些變化的影響,我決定比擬 localhost 的 HTTP 傳輸,看看是否可以看到任何速度差異.localhost 對于這個測試是很好的,因為沒有網(wǎng)絡(luò)速度限制,更快的 curl 下載也越快.服務(wù)器端也會相同的快/慢,因為我將使用相同的測試集進行這兩個測試.
我雷同方式構(gòu)建了 curl 7.53.1 和 curl 7.54.1-DEV,并運行這個命令:
curl http://localhost/80GB -o /dev/null
下載的 80GB 的數(shù)據(jù)會盡可能快地寫到空裝備中.
我獲得的確切數(shù)字可能不是很有用,因為它將取決于機器中的 CPU、使用的 HTTP 服務(wù)器、構(gòu)建 curl 時的優(yōu)化級別等,然則相對數(shù)字仍然應(yīng)該是高度相關(guān)的.新代碼對決舊代碼!
7.54.1-DEV 反復(fù)地表示出更快 30%!我的早期版本是 2200MB/秒增加到當(dāng)前版本的超過 2900 MB/秒.
這里的要點當(dāng)然不是說它很容易在我的機器上使用單一內(nèi)核以超過 20GB/秒的速度來進行 HTTP 傳輸,因為實際上很少有用戶可以通過 curl 做到這樣快速的傳輸.關(guān)鍵在于 curl 現(xiàn)在每個字節(jié)的傳輸使用更少的 CPU,這將使更多的 CPU 轉(zhuǎn)移到系統(tǒng)的其余部分來執(zhí)行任何必要做的事情.或者如果設(shè)備是便攜式設(shè)備,那么可以省電.
關(guān)于 malloc 的本錢:512MB 測試中,我使用舊代碼發(fā)生了 33832 次或更多的分配.舊代碼以大約 2200MB/秒的速率進行 HTTP 傳輸.這等于每秒 145827 次 malloc - 現(xiàn)在它們被消除了!600 MB/秒的改進意味著每秒鐘 curl 中每個減少的 malloc 操作能額外換來多傳輸 4300 字節(jié).
?
一點也不難,非常簡單.然而,有趣的是,在這個舊項目中,仍然有這樣的改進空間.我有這個想法已經(jīng)好幾年了,我很高興我終于花點時間來實現(xiàn).感謝我們的測試套件,我可以有相當(dāng)大的信心做這個“激烈的”內(nèi)部變化,而不會引入太可怕的回歸問題.由于我們的 API 很好地暗藏了內(nèi)部,所以這種變化可以完全不改變?nèi)魏闻f的或新的應(yīng)用程序……
(是的,我還沒在版本中發(fā)布該變更,所以這還有風(fēng)險,我有點懊悔我的“這很容易”的聲明……)
?
curl 的 git 倉庫從 7.53.1 到本日已經(jīng)有 213 個提交.即使我沒有別的想法,可能還會有一次或多次的提交,而不僅僅是內(nèi)存分配對性能的影響.
?
還有其他類似的環(huán)境么?
也許.我們不會做很多性能測量或比較,所以誰知道呢,我們也許會做更多的愚蠢事情,我們可以罷手并做得更好.有一個事情是我一直想做,但是從來沒有做,就是添加所使用的內(nèi)存/malloc 和 curl 執(zhí)行速度的每日“監(jiān)視” ,以便更好地跟蹤我們在這些方面不知不覺的回歸問題.
?
(關(guān)于我在 hacker news、Reddit 和其它處所讀到的關(guān)于這篇文章的評論)
有些人讓我再次運行那個 80GB 的下載,給出光陰.我運行了三次新代碼和舊代碼,其運行“中值”如下:
舊代碼:
real 0m36.705s
user 0m20.176s
sys 0m16.072s
新代碼:
real 0m29.032s
user 0m12.196s
sys 0m12.820s
承載這個 80GB 文件的服務(wù)器是尺度的 Apache 2.4.25,文件存儲在 SSD 上,我的機器的 CPU 是 i7 3770K 3.50GHz .
有些人也提到 alloca()
作為該補丁之一也是個辦理方案,但是 alloca()
移植性不夠,只能作為一個孤立的辦理方案,這意味著如果我們要使用它的話,需要寫一堆丑陋的 #ifdef
.
via: https://daniel.haxx.se/blog/2017/04/22/fewer-mallocs-in-curl/
作者:DANIEL STENBERG 譯者:geekpi 校對:wxy
本文由 LCTT 原創(chuàng)編譯,Linux中國 榮譽推出
本文永遠(yuǎn)更新鏈接地址:
《LINUX實操:減少 curl 中內(nèi)存分配操作(malloc)》是否對您有啟發(fā),歡迎查看更多與《LINUX實操:減少 curl 中內(nèi)存分配操作(malloc)》相關(guān)教程,學(xué)精學(xué)透。維易PHP學(xué)院為您提供精彩教程。
轉(zhuǎn)載請注明本頁網(wǎng)址:
http://www.fzlkiss.com/jiaocheng/7038.html