在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Jemalloc 的基本原理

Linux閱碼場 ? 來源:Linux閱碼場 ? 2023-05-30 09:16 ? 次閱讀

前言

C++ 語言中提供了大量的類庫和編程接口,雖然可以幫助開發者提升研發效率,但在特定場景下,其性能表現仍存在優化空間。開發者往往追求極致的代碼性能邏輯,一點點的優化改變就可以幫助業務獲得良好的性能收益。在字節降本提效的過程中,STE 團隊在算力監控系統中發現 Jemalloc 是業務的前五大 CPU 熱點基礎庫,具有很高的潛在性能優化空間。因此,從 2019 年開始對 Jemalloc 進行深度優化,并在字節內部進行了大范圍的優化落地,幫助業務團隊取得了較好的收益。本文將主要介紹 Jemalloc 的基本原理以及一些簡單易用的優化方法,幫助開發者在 Jemalloc 的實際應用中,獲得更好的性能表現。

內存相關概念簡介

Linux內存分配與分配器

當代 Linux 系統中可以同時運行多種多樣的進程,并且進程之間可以做到內存互相隔離,這得益于 Linux 的進程地址空間管理。

一個進程的地址空間中,包含了靜態內存、以及動態內存(常說的堆棧),棧的動態分配和釋放由編譯器完成,對于堆上內存,Linux 提供了 brk、sbrk、mmap、munmap 等系統調用來進行內存分配和釋放,但是這些函數的直接使用會帶來不小的理解門檻和使用復雜性,如 brk 需要指定堆的上界地址,容易出現內存錯誤;mmap 直接申請 pagesize 為單位的內存,對于小于此內存的分配會造成極大的內存浪費。因此需要有內存分配器來輔助管理堆的動態申請和釋放。

通常內存分配器如 ptmalloc提供了 malloc 等函數來進行內存的分配,free 進行內存釋放,這些函數在底層調用了 brk、mmap 等函數申請內存,并以地址的形式返回給用戶,用戶在 malloc、free 匹配的情況下不必擔心在分配和釋放時出現內存錯誤或者內存浪費。

內存碎片

雖然內存分配器在一定程度上保證了內存的利用率,但是不可避免地會出現內存碎片,包括了內存頁內碎片和內存頁間碎片,碎片的產生會導致部分內存不可用,內存碎片的大小也是評估一個內存分配器好壞的重要指標。

常見的內存分配管理算法

堆上內存以鏈式形式存在,最簡單的動態內存分配算法有:

First fit:尋找找第一個滿足請求 size 的內存塊做分配

Next fit:從當前分配的地址開始,尋找下一個滿足請求 size 的空閑塊

best fist:對空閑塊進行排序,然后找第一個滿足要求的空閑塊

另外還有 Buddy 算法和 Slab 算法,也是 jemalloc 中用到的核心算法:

Buddy allocation

e6e17012-fe80-11ed-90ce-dac502259ad0.png

Buddy 算法簡單來說如上圖,一般 2 的 n 次冪大小來管理內存,當申請的內存 size 較小,且當前空閑內存塊均大于 size 的兩倍,那么會將較大的塊分裂,直到分裂出大于size,并小于 size * 2的塊為止;當內存 size 較大時則相反,會將空閑塊不斷合并。

Buddy 算法沒有塊間內存碎片,但是塊內內存碎片較大,可以看到當申請 2KB+1B 的 size 時,需要用 4KB 的內存塊,內存碎片最壞情況可達 50%。

Slab allocation

調用 Linux 系統調用進行內存的分配和釋放會讓程序陷入內核態,帶來不小的性能開銷,slab 算法應運而生。每個 slab 都是一塊連續內存,并將其劃分成大小相同的 slots,用 bitmap 來記錄這些 slots 的空閑情況,內存分配時,返回一個 bitmap 中記錄的空閑塊,釋放時,將其記錄忙碌即可,而 slab size 和 slot size 是內存碎片大小的關鍵。

Jemalloc簡介

Jemalloc 是 malloc(3) 的實現,在現代多線程、高并發的互聯網應用中,有良好的性能表現,并提供了優秀的內存分析功能。

Jemalloc 主要有以下幾個特點:

高效地分配和釋放內存,可以有效提升程序的運行速度,并且節省 CPU 資源

盡量少的內存碎片,一個長穩運行地程序如果不控制內存碎片的產生,那么可以預見地這個程序會在某一時刻崩潰,限制了程序的運行生命周期

支持堆的 profiling,可以有效地用來分析內存問題

支持多樣化的參數,可以針對自身地程序特點來定制運行時 Jemalloc 各個模塊大小,以獲得最優的性能和資源占用比

下文主要介紹了 Jemalloc 的內存分配算法、數據結構,以及一些針對具體程序的優化實踐和建議。

Jemalloc核心算法與數據結構

Jemalloc 整體的算法和數據結構基于高效和低內存碎片的原則進行設計,主要體現在:

隔離了大 Size 和小 Size 的內存分配(區分默認閾值為 3.5 個 Pagesize),可以有效地減少內存碎片

在內存重用時默認使用低地址,并將內存控制在盡量少的內存頁上

制定 size class 和 slab class,以便減少內存碎片

嚴格限制 Jemalloc 自身的元數據大小

用一定數量的 arena 來管理內存的單元,每個 arena 管理相當數量的線程,arena 之間獨立,盡量減少多線程時鎖競爭

我們來看下 Jemalloc 是如何來實現這些特性的。

Extent

Jemalloc 的內存管理結合了 buddy 算法和 slab 算法。Jemalloc 引入 extent 的概念,extent 是 arena 管理的內存對象,在 large size 的 allocation 中充當 buddy 算法中的 chunk,small size allocation 中,充當 slab。

每個 extent 的大小是 Pagesize 的整數倍,不同 size 的 extent 會用 buddy 算法來進行拆分和合并,大內存的分配會直接使用一整個的 extent 來存儲。小內存的分配使用的是 slab 算法,slab size 的計算規則為 size 和 pagesize 的最小公倍數,因此每個extent總是可以存儲整數倍個對應 size。

extent 本身設置 bitmap,來記錄內存占用情況,以及自身的各種屬性,同類型的 extents 用 paring heap 存儲。

此外,arena 將 extent 分為多種類型,有當前正在使用未被填滿的extent,有一段時間未使用的dirty extent,還有長時間未使用的muzzy extent,以及開啟retained功能后的retained extent,extent分類的作用相當于多級緩存,當線程內存分配壓力較小時,空余的extent會被緩存,以備壓力增大時使用,可以避免與操作系統的交互。

Small size align and Slab size

為了減少頁內內存碎片,Jemalloc 對 small size 進行了對齊,對于每一個 size,以二進制的視角來看,將其分為兩個數:group、mod。group 表示 size 的二進制最高位,如果 size 正好為 2 的冪次,則將其分在上一個 group 中;mod 表示最高位的后兩位,有 0、1、2、3 共 4 種可能。這樣構成的 align 后的 size 在同一個 group 中步長(即兩個相鄰 mod 計算得到的 size 之間的差值)相同,group 越大,步長會呈 2 的倍數增長。

如下圖,框中的 4個是同一個 group 中 4 種 mod 在 align 后的 size,其中 160 表示包含了 129B 到 160B 在對齊后的大小:

e723f4fa-fe80-11ed-90ce-dac502259ad0.png

計算出 aligned size 后,就需要計算 slab size,每個 slab size 為 pagesize 和 aligned size 的最小公倍數,以防止跨 slab 的 size 或者 slab 無法被填滿的情況出現。以 4K page 為例,128B 的 slab size 即 4K,160B 的 slab size 為 20K。

Tcache and arena

為了減少多線程下鎖的競爭,Jemalloc 參考 lkmalloc 和 tcmalloc,實現了一套由多個 arena 獨立管理內存加 thread cache 的機制,形成 tcache 有空余空間時不需要加鎖分配,沒有空余空間時將鎖控制在線程所屬 arena 管理的幾個線程之間的模式。

e77ab0d8-fe80-11ed-90ce-dac502259ad0.png

tcache 中每一個 size 對應一個 bin,當 tcache 需要填充時,在 arena 中發生的如下圖:

e7b9cf84-fe80-11ed-90ce-dac502259ad0.png

allocation/dallocation in tcache

tcache 以 thread local storage對象的形式存儲,主要服務于 small size 和一小部分 large size。

當 tcache 中有空閑時,一次 malloc 的過程很簡單:

對 size 做 align 得到 usize

查找 usize 對應的 bin,bin 為 tcache 中針對不同 size 設置的 slots

bin 有空閑地址則直接返回,沒有空閑地址則會向 arena 請求填充

每個 bin 的結構如下圖,avail 指向 bin 的起始地址,ncached 初始為 bin 的最大值 ncached_max (與 slab size 相關,最小為 20 最大為 200),每次申請內存會返回 ncached 指向的地址并自減1,直到小于限制值。

e7e4af38-fe80-11ed-90ce-dac502259ad0.png

釋放的時候相反,當 tcache 不為空,即 ncached 不等于 bin 的 ncached_max 時,ncached 自加1,并且將 free 的地址填入 bin 中。

Tcache fill

上面的 allocation 過程是 tcache 中有足夠的空閑塊供分配,當 tcache 中已經沒有空閑塊時,會向其所屬的 arena 申請 fill,此時 arena 中會加鎖去分級 extent 取空閑塊,并把當前使用的 extent 移入full extent。

Tcache flush

當 dallocate 觸發 tcache 中又沒有分配任何內存,即 ncached_max 等于 ncached_max 時,tcache 會觸發 flush,flush 會將 tcache 中一半的內存釋放回原 extent,即將 tache 的可用空間壓縮到原來的一半,這個過程中也會加對應 extent 的鎖以保證同步。

Jemalloc優化思路

從上一章節可以看到,jemalloc 對于內存用的是多級緩存的思路,tcache 的代價最小,無須加鎖可以直接返回;其次是 arena 的 bin->extent,鎖的粒度在對應的 bin 上,會是 bin 對應的 size 在這個 arena 中無法再做 fill 或 flush;然后是 dirty extent、muzzy extent,這部分是 arena 全局加鎖,會鎖住其他線程的 fill 或者 purge,那么在多線程下,我們可以用幾個思路來優化鎖的競爭。

arena優化

從上一章節可知,jemalloc 將鎖的范圍都控制在 arena 中,每個 arena 會管理一系列線程,線程在 arena 中是平均分配的,arena 默認數量是 CPU 個數 * 4。因此,當我們在一臺 8 核的機器上運行 256 個線程時,意味著每個arena 需要管理 8 個線程,這些線程在內存任務繁重時會產生嚴重的鎖競爭,從而影響性能。此時可選擇使用 malloc_conf128,增加 arena 數量到 128 個,每個 arena 只需管理 2 個線程,線程之間產生鎖競爭的概率就會大大減小。

此外還可以選擇用 mallocx 隔離線程,讓內存分配任務較重的線程獨占 arena。

Slab size優化

Slab size 的大小如上所述,為 usize 大小和 pagesize 的最小公倍數,這一機制可以保證減少內存碎片,但是tcache 的 fill 與 flush 都與 slab size 相關,一個和業務內存模型匹配的 slab class 才可以得到最好的性能效果。

下面是一張 jemalloc 和 ptmalloc 的對比圖,可以看到在 1024 以下的性能 jemalloc 都優于 ptmalloc,但是jemalloc 自身的性能明顯存在波動,幾個波動出現在 128B、256B、512B 以及 1024B 周圍,因為這些 size 本身就是 pagesize 的因子或者公因子較多,所以 slab size 占用的 page 數也相對較少,fill 和 flush 所需要的slab數也越多。

e8297f6e-fe80-11ed-90ce-dac502259ad0.png

dirty decay & muzzy decay

盡管我們希望將所有的 malloc、free 內存都可以放在 tcache 中或者 bin 中,這樣可以最大化執行效率,但是實際的程序中這很難做到,因為每個線程都需要增加內存,會造成不小的內存壓力,而且內存的申請釋放往往會有波峰,dirty extents 和 muzzy extents 就可以來應對這些內存申請的波峰,而避免需要轉入內核態來重新申請內存頁。

dirty_decay_ms 和 muzzy_decay_ms 是 jemalloc 中用來控制長時間空閑內存衰變的時間參數,適當地擴大 dirty decay 的時間可以有效地解決性能劣化的尖刺。

Tcache ncached_max

tcache 中每一個 bin 的 slots 數量由 ncached_max 決定,當 tcache 中 ncached_max 耗盡時會觸發 arena 的 fill tcache 而產生鎖,而 ncached_max 的大小默認為 2 * slab size,最小為 20,最大為 200,適當地擴大 ncached_max 值可以在一些線程上形成更優的 allocation/deallocation 循環(5.3版本已支持用malloc_conf進行更改)。

優化方法:調優三板斧

結合以上優化思路,通過以下步驟對應用進行調優:

Dump stats

在 long exist 的程序中可調用 jemalloc 的 malloc_stats_print 函數,dump 出應用當前內存分配信息

// reference: https://jemalloc.net/jemalloc.3.html
void malloc_stats_print(void (*write_cb)(void *, const char *), // 回調函數,可以寫入文件
                        void *cbopaque, // 回調函數參數
                        const char *opts); // stats的一些選項,如"J"是導出json格式

或通過設置 malloc_conf,在程序運行結束后自動 dump stats:

export MALLOC_CONF=stats_print:true
stats 分析

用 Json 格式 dump stats 后,可以得到如下圖所示結構的 json 文件:

e8abb29a-fe80-11ed-90ce-dac502259ad0.png

各字段含義可參考:
https://jemalloc.net/jemalloc.3.html

按上一章節思路,可主要關注以下幾點: (1)arena 數量與 threads 數量比例

arena 數:jemalloc->arenas->narenas

threads 數:jemalloc->stats.arenas->merged->nthreads

分析:

threads : arenas 比例代表了單個 arena 中管理的線程數,將 malloc、free 較多,并且有可能產生競爭的線程盡量獨占 arena。

(2)各個 extent 中 mutex 開銷

jemalloc->stats.arenas->merged->mutexes

分析

該節點中的 mutex 操作次數、等鎖時間可以反映出該類型 extent 的鎖競爭程度,若 extent_retained 鎖競爭嚴重,可適當調大 muzzy_decay_ms;同理,當 extents_muzzy 鎖競爭嚴重,可適當調大 diry_decay_ms;extents_dirty 鎖競爭嚴重,可適當調大 ncached_max,讓malloc 盡量可以在 tcache 中完成

(3)arena 中各個 bin 的 malloc、free 次數

jemalloc->stats.arenas->merged->bins

分析

bin 中的 nfills 可以反映該 slab 填充的次數,針對 regions 本身較少,nfill 次數又多的 size,如 521B、1024B、2048B、4096B 等,可適當調大 slab size 來減小開銷

添加MALLOC_CONF參數或修改代碼

MALLOC_CONF 是 jemalloc 中用來動態設置參數的途徑,無須重新編譯二進制,可以通過 MALLOC_CONF 環境變量或者 /etc/malloc_conf 軟鏈接形式設置,參數之間用 ',' 分割。除需要使用線程獨占的 arena 外,以上其他優化均可通過 MALLOC_CONF 配置來完成。

(1)arena優化方法 narenas 設置:

export MALLOC_CONF=narenas:xxx  # xxx最大為1024

ln -s "narenas:xxx" /etc/malloc_conf

設置線程獨占的 arena:

unsigned thread_set_je_exclusive_arena() {
  unsigned arena_old, arena_new;
  size_t sz = sizeof(unsigned);


  /* Bind to a manual arena. */
  if (mallctl("arenas.create", &arena_new, &sz, NULL, 0)) {
    std::cout << "Jemalloc arena create error
";
    return 0;
  }
  if (mallctl("thread.arena", &arena_old, &sz, &arena_new, sizeof(arena_new))) {
    std::cout << "Thread bind to jemalloc arena error
";
    return 0;
  }
  return arena_new;
}

(2)各類大小優化方法

dirty extents:

export MALLOC_CONF=dirty_decay_ms:xxx  # -1為不釋放dirty extents,易發生OOM

muzzy extents:

export MALLOC_CONF=muzzy_decay_ms:xxx  # -1為不釋放muzzy extents,易發生OOM

tcache ncached_max 調整,ncached_max 與 slab size 相關,計算方式為

(slab_size / region_size) << lg_tcache_nslots_mul (默認值1)
最大限值為 tcache_nslots_small_max(默認200),最小限值為 tcache_nslots_small_min(默認20)。

如調整 32B 的 ncached_max,當前系統 page size 為 4K,計算默認的 ncached_max 的方法:

(slab_size / region_size) << lg_tcache_nslots_mul = (4096 / 32) << 1 = 256
超過了 tcache_nslots_small_max,所以 32B 的 ncache_max 默認即為 200。

調整 ncached_max 默認值相關參數:

export 
MALLOC_CONF=tcache_nslots_small_min:xxx,tcache_nslots_small_max:xxx,lg_tcache_nslots_mul:xxx

Slab size 設置方法:

export MALLOC_CONF="slab_sizes17|100-200:1|128-128:2" # -左右表示size范圍,:后設置page數,|分割各個不同的size范圍

字節業務優化案例

Jemalloc 的 stats dump 已經集成到監控系統中,經過分析發現,字節內部的應用普遍線程數量較多,在 arena 中的鎖競爭比較激烈,并且 allocte/deallocte 集中在某些線程中,因此可以通過讓核心線程獨占 arena 來完成優化。以其中一個業務在平臺上的 stats 數據為例:

e8dabafe-fe80-11ed-90ce-dac502259ad0.png 可以看到進程總線程數為 1776 個,arenas 數量為 256 個,平均每個 arena 中的內存需要有 7-8 個線程共享,再查看 mutexs 的 stats : e9271fa2-fe80-11ed-90ce-dac502259ad0.png

可以看到在 extents 中產生鎖的開銷并不小,首先選擇擴大 arenas 的數量,從 256 擴大到 1024 個,發現 CPU 相對下降了 4.5%,但是相對的內存上漲了 10%,在分析代碼后,發現 allocate/deallocate 較多的線程總數量只有80+,針對這些線程通過 mallctl 單獨創建了 arena,并綁定 tcache,并調小其他線程的 muzzy_decay_ms,最終為該業務節省了 4% 的 CPU 收益,內存基本持平。

總結

最好的基礎庫總是通用的,最適合的基礎庫總是最個性化的。對于 jemalloc 在業務上的優化與實踐,STE團隊進行不斷探索,采集內存信息并進行平臺化展示,以便業務及時發現自身程序在 jemalloc 上的性能瓶頸,并做出針對性地調優,目前已在 10+ 業務上進行參數優化平均幫助各業務團隊節省了 3% 的 CPU(jemalloc 在部分業務中平均占用 10% ~ 15% 的CPU)。未來 STE 團隊將繼續深入 jemalloc 性能優化,探索定制化的業務內存分配和管理方案,以獲得更好的優化成果。

審核編輯:彭靜
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 接口
    +關注

    關注

    33

    文章

    8667

    瀏覽量

    151526
  • C++
    C++
    +關注

    關注

    22

    文章

    2113

    瀏覽量

    73742
  • malloc
    +關注

    關注

    0

    文章

    53

    瀏覽量

    75

原文標題:Jemalloc內存分配與優化實踐

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    步進電機基本原理

    本帖最后由 eehome 于 2013-1-5 09:48 編輯 步進電機基本原理
    發表于 08-16 16:17

    串聯諧振逆變器的基本原理

    串聯諧振通常伴有逆變器。該組合稱為串聯諧振逆變器。什么是基本原理?讓我簡要介紹串聯諧振逆變器的一些基本原理。]首先給你看一張圖片:
    發表于 11-07 10:21

    IC測試基本原理是什么?

    IC測試基本原理是什么?ATE測試向量是什么?
    發表于 05-07 06:43

    IC測試的基本原理是什么?

    本文詳細介紹了芯片開發和生產過程中的IC測試基本原理
    發表于 05-08 07:33

    TSP控制的基本原理是什么?

    TSP控制的基本原理是什么?
    發表于 05-11 06:39

    什么是PWM?PWM的基本原理是什么?

    什么是PWM?PWM的基本原理是什么?PWM輸出配置有哪些步驟?
    發表于 06-30 07:39

    電機轉動的基本原理是什么?

    電機轉動的基本原理是什么?電機運動的基本原則有哪些?
    發表于 07-21 07:59

    線性電源的基本原理是什么

    多路線性電源 AC-DC穩壓電源 低紋波電源 可調線性電源 原理圖PCB目錄多路線性電源 AC-DC穩壓電源 低紋波電源 可調線性電源 原理圖PCB基本原理芯片選型原理圖&3D-PCB具體
    發表于 07-30 07:47

    無線充電的基本原理是什么

    一 、無線充電基本原理無線充電的基本原理就是我們平時常用的開關電源原理,區別在于沒有磁介質耦合,那么我們需要利用磁共振的方式提高耦合效率,具體方法是在發送端和接收端線圈串并聯電容,是發送線圈處理諧振
    發表于 09-15 06:01

    RAID技術的基本原理是什么

    RAID技術的基本原理是什么?RAID技術有哪幾個優勢?
    發表于 10-14 12:01

    IIC的基本原理是什么?

    IIC的基本原理是什么?
    發表于 11-25 08:46

    串口通信基本原理是什么?

    串口通信基本原理是什么?串行通信的分類有哪些?
    發表于 12-03 06:08

    串口通信的基本原理是什么?

    同步通信和異步通信的區別是什么?串口通信的基本原理是什么?
    發表于 12-13 06:46

    步進馬達基本原理

    步進馬達基本原理步進馬達基本原理步進馬達基本原理
    發表于 11-30 11:55 ?8次下載

    LLC基本原理及設計方法

    LLC基本原理及設計方法
    發表于 06-25 10:05 ?7次下載
    主站蜘蛛池模板: 免费黄色在线观看| 久久综合久色欧美婷婷| 男生女生靠逼视频| 黑人一区二区三区中文字幕| 男人的亚洲天堂| 999影院成 人在线影院| 在线观看www日本免费网站| 伊人久久成人| 婷婷久久综合九色综合98| 色天使色护士| 黄色网址中文字幕| 精品亚洲午夜久久久久| 国产精品天天看大片特色视频 | 亚洲天堂三级| 特别毛片| 男人和女人做免费做爽爽视频| 久久e| 丁香婷婷色综合| 亚洲第一成人在线| 国产福利你懂的| 久久久噜噜噜| 国产caoni111在线观看视频| 五月天婷婷在线播放| 国产成人综合亚洲怡春院| 狠狠干狠狠干狠狠干| 视频免费在线观看| 黄视频网站免费看| 四虎影院视频| 狠狠狠| 天天插夜夜操| 日本免费人成黄页网观看视频| 国产美女久久久久| 性做久久久久久网站| 亚洲国产成人久久一区久久| 欧美成人伊人久久综合网| 成人的天堂视频一区二区三区| 日日舔夜夜操| h视频在线免费观看| 网红和老师啪啪对白清晰| 美日韩中文字幕| 天天做天天干|