【中間件】bthread效率為什么高?

bthread效率為什么更高?

1 基本概念

bthread是brpc中的用戶態線程(也可稱為M:N線程庫),目的是:提高程序的并發度,同時降低編碼難度,在多核cpu上提供更好的scalability和cache locality。其采用M:N模型,即多個用戶線程(bthread)映射到少量的系統線程(pthread)上。
linux當下的pthread實現(NPTL)是1:1的,M個bthread也相當于映射至N個LWP。
bthread前身是Distributed Process(DP)中的fiber,一個N:1的合作式線程庫,等價于event-loop庫,但是同步方式。

2 高效做法

  1. 用戶態調度:避免內核態和用戶態之間的切換開銷,上下文切換更快。系統線程的切換需要內核接入,而用戶態線程的切換完全在用戶空間完成,減少了系統調用和上下文切換的開銷。
  2. 更輕量級的上下文切換:用戶態線程的上下文數據量風小,只需要保存必要的寄存器狀態,而內核線程需要保存更多的狀態信息,比如浮點寄存器、信號處理器等。
  3. M:N模型:多個用戶線程由較少的系統線程調度,減少了系統線程的創建和銷毀開銷,同時也減少了上下文切換的次數。系統線程的數量通常與cpu核心數相當,避免了過多的線程競爭。
  4. 無鎖或細粒度鎖的數據結構:任務隊列使用無鎖隊列或細粒度鎖,減少了線程間的競爭和等待時間,提高了并發性能。
  5. 工作竊取(work stealing):當某個工作線程的任務隊列為空時,可以從其他線程的隊列中竊取任務,實現負載均衡,避免線程空閑,提高資源利用率。
  6. 定制化的內存池管理:采用內存池技術,復用棧空間,減少內存分配和釋放的開銷,避免頻繁的系統調用。
  7. 避免阻塞系統調用:通過異步IO或非阻塞IO配合事件驅動,減少了線程因IO操作而阻塞的情況,提高了CPU利用率。

進一步解釋

  1. 用戶態調度
    避免內核陷入;
    能夠實現0系統調用(無需內核調度器);
類型上下文切換時長操作
用戶態50 - 100 ns僅需保存/恢復必要的寄存器(約10個reg)
內核態1-5 us保存完整的上下文(浮點寄存器、信號處理器等);切換內核態堆棧
  1. M:N模型
維度M:N模型1:1模型(eg. pthread)
線程數量百萬級用戶線程千級系統線程
調度開銷用戶態協作式調度內核搶占式調度
內存占用每個線程約4-64KB棧每個線程約2-10MB
創建/銷毀成本微秒級(純用戶態操作)毫秒級(需內核參與)
  1. 任務調度策略
  • 工作竊取算法
Task *steal_task()
{for (Worker &w : other_workers) {if (Task *t = w.queue.try_steal()) {return t;}}return nullptr;
}// 每個worker線程維護本地任務隊列
// 空閑worker從其他worker的隊列尾部竊取任務
// 減少鎖競爭,提高CPU緩存命中率
  • 協作式調度
    顯式yield讓出cpu;
    避免不必要的搶占,減少上下文切換;
  1. 內存管理優化
  • 棧內存復用
class StackPool {
public:static constexpr int MAX_CACHED_STACKS = 1000;std::vector<void*> cached_stacks;void *alloc() {if (!cached_stacks.empty()) {return pop_back();}return ::malloc(STACK_SIZE);}void free(void *stack) {if (cached_stacks.size() < MAX_CACHED_STACKS) {cached_tasks.push_back(stack);} else {::free(stack);}}
};// 減少頻繁的malloc/free
// 避免內存碎片

** 個人疑問?**
棧內存復用的場景是什么?

  1. 與異步I/O深度集成
  • 事件驅動架構
void async_read(int fd, void *buf, size_t size) {epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, ...);bthread_yield();// IO完成后由事件循環喚醒
}// 通過epoll/kqueue實現非阻塞IO
// IO等待期間自動yield,不阻塞worker線程

3 性能對比數據

參考網絡數據,本人未驗證。

場景bthread吞吐量pthread吞吐量
10k空循環任務1.2M tasks/sec120K tasks/sec
網絡代理(1KB包)850K req/sec65K req/sec
數據庫訪問720K QPS45K QPS

4 bthread適用場景

  1. 高并發網絡服務(eg. web服務器、rpc框架)
  2. 大規模并行計算(eg. 分布式任務調度)
  3. 低延遲交易系統(eg. 金融訂單處理)
  4. 資源受限環境(eg. 嵌入式設備)

5 代價與限制

  • 開發復雜度高
    eg. 需要手動處理yield點

  • 無法利用多核并行
    單個worker線程仍綁定單個cpu核心

  • 調試困難
    用戶態線程的堆棧跟蹤不如內核線程直觀

6 匯總原因

bthread的高效源自現代多核硬件和網絡服務特征的深度優化,通過減少不必要的內核交互、精細化資源管理和智能調度策略,在特定場景下可帶來數量級的性能提升。

7 FAQ

7.1 bthread不是coroutine

我們常說的協程特指N:1線程庫,即所有的協程運行于一個系統線程中,計算能力和各類eventloop庫等價。由于不跨線程,協程之間的切換不需要系統調用,可以非常快(100ns-200ns),受cache一致性的影響也小。但代價是協程無法高效地利用多核,代碼必須非阻塞,否則所有的協程都被卡住,對開發者要求苛刻。協程的這個特點使其適合寫運行時間確定的IO服務器,典型如http server,在一些精心調試的場景中,可以達到非常高的吞吐。但百度內大部分在線服務的運行時間并不確定,且很多檢索由幾十人合作完成,一個緩慢的函數會卡住所有的協程。在這點上eventloop是類似的,一個回調卡住整個loop就卡住了,比如ubaserver(注意那個a,不是ubserver)是百度對異步框架的嘗試,由多個并行的eventloop組成,真實表現糟糕:回調里打日志慢一些,訪問redis卡頓,計算重一點,等待中的其他請求就會大量超時。所以這個框架從未流行起來。

bthread是一個M:N線程庫,一個bthread被卡住不會影響其他bthread。關鍵技術兩點:work stealing調度和butex,前者讓bthread更快地被調度到更多的核心上,后者讓bthread和pthread可以相互等待和喚醒。這兩點協程都不需要。

7.2 最好不要在用戶程序中調用bthread

除非你需要在一次RPC過程中讓一些代碼并發運行,你不應該直接調用bthread函數,把這些留給brpc做更好。

7.3 bthread和pthread woker

pthread worker在任何時間只會運行一個bthread,當前bthread掛起時,pthread worker先嘗試從本地runqueue彈出一個待運行的bthread,若沒有,則隨機偷另一個worker的待運行bthread,仍然沒有才睡眠并會在有新的待運行bthread時被喚醒。

7.4 bthread中能調用阻塞的pthread或系統函數

只阻塞當前pthread worker。其他pthread worker不受影響

若bthread因bthread API而阻塞,它會把當前pthread worker讓給其他bthread。若bthread因pthread API或系統函數而阻塞,當前pthread worker上待運行的bthread會被其他空閑的pthread worker偷過去運行

7.5 pthread可以調用bthread API

bthread API在bthread中被調用時影響的是當前bthread,在pthread中被調用時影響的是當前pthread。使用bthread API的代碼可以直接運行在pthread中。

7.6 若有大量的bthread調用了阻塞的pthread或系統函數,會影響RPC運行

比如有8個pthread worker,當有8個bthread都調用了系統usleep()后,處理網絡收發的RPC代碼就暫時無法運行了。只要阻塞時間不太長, 這一般沒什么影響, 畢竟worker都用完了, 除了排隊也沒有什么好方法. 在brpc中用戶可以選擇調大worker數來緩解問題, 在server端可設置ServerOptions.num_threads或-bthread_concurrency, 在client端可設置-bthread_concurrency.

規避方法

  • 一個容易想到的方法是動態增加worker數. 但實際未必如意, 當大量的worker同時被阻塞時, 它們很可能在等待同一個資源(比如同一把鎖), 增加worker可能只是增加了更多的等待者.
  • 那區分io線程和worker線程? io線程專門處理收發, worker線程調用用戶邏輯, 即使worker線程全部阻塞也不會影響io線程. 但增加一層處理環節(io線程)并不能緩解擁塞, 如果worker線程全部卡住, 程序仍然會卡住, 只是卡的地方從socket緩沖轉移到了io線程和worker線程之間的消息隊列. 換句話說, 在worker卡住時, 還在運行的io線程做的可能是無用功. 事實上, 這正是上面提到的沒什么影響真正的含義. 另一個問題是每個請求都要從io線程跳轉至worker線程, 增加了一次上下文切換, 在機器繁忙時, 切換都有一定概率無法被及時調度, 會導致更多的延時長尾.
  • 一個實際的解決方法是限制最大并發, 只要同時被處理的請求數低于worker數, 自然可以規避掉"所有worker被阻塞"的情況.
  • 另一個解決方法當被阻塞的worker超過閾值時(比如8個中的6個), 就不在原地調用用戶代碼了, 而是扔到一個獨立的線程池中運行. 這樣即使用戶代碼全部阻塞, 也總能保留幾個worker處理rpc的收發. 不過目前bthread模式并沒有這個機制, 但類似的機制在打開pthread模式時已經被實現了. 那像上面提到的, 這個機制是不是在用戶代碼都阻塞時也在做"無用功"呢? 可能是的. 但這個機制更多是為了規避在一些極端情況下的死鎖, 比如所有的用戶代碼都lock在一個pthread mutex上, 并且這個mutex需要在某個RPC回調中unlock, 如果所有的worker都被阻塞, 那么就沒有線程來處理RPC回調了, 整個程序就死鎖了. 雖然絕大部分的RPC實現都有這個潛在問題, 但實際出現頻率似乎很低, 只要養成不在鎖內做RPC的好習慣, 這是完全可以規避的.

7.7 bthread沒有channel

channel代表的是兩點間的關系,而很多現實問題是多點的,這個時候使用channel最自然的解決方案就是:有一個角色負責操作某件事情或某個資源,其他線程都通過channel向這個角色發號施令。如果我們在程序中設置N個角色,讓它們各司其職,那么程序就能分類有序地運轉下去。所以使用channel的潛臺詞就是把程序劃分為不同的角色。channel固然直觀,但是有代價:額外的上下文切換。做成任何事情都得等到被調用處被調度,處理,回復,調用處才能繼續。這個再怎么優化,再怎么尊重cache locality,也是有明顯開銷的。另外一個現實是:用channel的代碼也不好寫。由于業務一致性的限制,一些資源往往被綁定在一起,所以一個角色很可能身兼數職,但它做一件事情時便無法做另一件事情,而事情又有優先級。各種打斷、跳出、繼續形成的最終代碼異常復雜。

我們需要的往往是buffered channel,扮演的是隊列和有序執行的作用,bthread提供了ExecutionQueue,可以完成這個目的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/81209.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/81209.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/81209.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

DeepSeek V2:引入MLA機制與指令對齊

長上下文革命:Multi-Head Latent Attention(MLA)機制 傳統 Transformer 的多頭注意力需要緩存所有輸入token的 Key 和 Value,這對長文本推理時的內存開銷極為龐大。DeepSeek V2 針對這一難題提出了“Multi-Head Latent Attention”(MLA)機制。MLA 的核心思想是對多頭注意…

Druid監控sql導致的內存溢出--內存分析工具MemoryAnalyzer(mat)

問題 druid監控sql在網頁端顯示&#xff0c;我的服務插入sql比較大&#xff0c;druid把執行過的sql保存在DruidDataSource類的成員變量JdbcDataSourceStat dataSourceStat&#xff1b; JdbcDataSourceStat類中的LinkedHashMap<String, JdbcSqlStat> sqlStatMap中&#…

《Python實戰進階》No45:性能分析工具 cProfile 與 line_profiler

Python實戰進階 No45&#xff1a;性能分析工具 cProfile 與 line_profiler 摘要 在AI模型開發中&#xff0c;代碼性能直接影響訓練效率和資源消耗。本節通過cProfile和line_profiler工具&#xff0c;實戰演示如何定位Python代碼中的性能瓶頸&#xff0c;并結合NumPy向量化操作…

計算機操作系統知識集合

主要來自小林coding 硬件結構 cpu位寬 如果用 32 位 CPU 去加和兩個 64 位大小的數字&#xff0c;就需要把這 2 個 64 位的數字分成 2 個低位 32 位數字和 2 個高位 32 位數字來計算&#xff0c;先加個兩個低位的 32 位數字&#xff0c;算出進位&#xff0c;然后加和兩個高位…

電機常用易混淆概念說明(伺服、舵機、多輪)

1. 概述 基礎動力需求 &#xff1a;普通電機&#xff08;如水泵、風扇&#xff09;。 高精度控制 &#xff1a;優先伺服系統或伺服電機&#xff08;如數控機床&#xff09;。 微型化場景 &#xff1a;舵機&#xff08;如遙控模型&#xff09;。 移動底盤 &#xff1a;單舵輪成…

進程與線程:04 內核線程

內核級線程概述 上一講我們學習了用戶級線程&#xff0c;了解了其切換和創建方式。用戶級線程切換核心在于從一個棧變為兩個棧&#xff0c;每個線程有自己的棧和線程控制塊&#xff08;tcb&#xff09;&#xff0c;切換時先切換tcb再切換棧&#xff0c;創建時將切換的pc指針放…

信息系統項目管理師-軟考高級(軟考高項)???????????2025最新(六)

個人筆記整理---僅供參考 第六章項目管理概論 6.1PMBOK的發展 6.2項目基本要素 組織過程資產指的是項目上的&#xff0c;國產數據庫的使用----安保和安全指的是環境因素 6.3項目經理的角色 6.4價值驅動的項目管理知識體系

[藍橋杯 2023 國 Python B] 劃分 Java

import java.util.*;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int[] arr new int[41];int sum 0;for (int i 1; i < 40; i) {arr[i] sc.nextInt();sum arr[i];}sc.close();int target sum / 2; // 最接近的兩…

Redis05-進階-主從

零、文章目錄 Redis05-進階-主從 1、搭建主從架構 &#xff08;1&#xff09;概述 單節點Redis的并發能力是有上限的&#xff0c;要進一步提高Redis的并發能力&#xff0c;就需要搭建主從集群&#xff0c;實現讀寫分離。 &#xff08;2&#xff09;集群概況 我們搭建的主從…

小結:ipsec-ike

IPSec 手動配置與自動配置&#xff08;IKE動態協商&#xff09; 手動配置IPSec 邏輯圖 #mermaid-svg-eNMnNEwnoTjF8fkV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-eNMnNEwnoTjF8fkV .error-icon{fill:#552222;}…

瀟灑郎: 100% 成功搭建Docker私有鏡像倉庫并管理、刪除鏡像

1、Registry Web管理界面 2、拉取Registry-Web鏡像 創建配置文件 tee /opt/zwx-registry/web-config.yml <<-EOF registry:url: http://172.28.73.90:8010/v2name: registryreadonly: falseauth:enabled: false EOF 拉取docker-registry-web鏡像并綁定Registry倉庫 …

《機器學習中的過擬合與模型復雜性:理解與應對策略》

《機器學習中的過擬合與模型復雜性&#xff1a;理解與應對策略》 摘要 在機器學習中&#xff0c;過擬合是模型在訓練數據上表現良好但在新數據上泛化能力差的現象。本文深入探討了過擬合與模型復雜性之間的關系&#xff0c;分析了復雜模型導致過擬合的原因&#xff0c;并介紹…

linux中sigint和sigterm的區別

SIGINT 和 SIGTERM 是在 Unix 及類 Unix 系統&#xff08;包括 Linux&#xff09;中用于進程間通信的信號&#xff0c;它們都可以用于請求進程終止&#xff0c;區別如下&#xff1a; 1、信號編號與定義 在信號機制里&#xff0c;每個信號都有對應的編號&#xff0c;這便于系統…

一套SaaS ERP管理系統源碼,支持項目二開商用,SpringBoot+Vue+ElementUI+UniAPP

ERP管理系統源碼&#xff0c;一款適用于小微企業的SaaS ERP管理系統源碼, 采用最新的技術棧開發(SpringBootVueElementUIUniAPP)&#xff0c;讓企業簡單上云。 專注于小微企業的應用需求&#xff0c;如企業基本的進銷存、詢價&#xff0c;報價, 采購、銷售、MRP生產制造、品質…

2025 新生 DL-FWI 培訓

摘要: 本貼給出 8 次討論式培訓的提綱, 每次培訓 1 小時. 1. Basic concepts 主動學習: 提問, 理解, 繼續追問. 通過不斷迭代, 逐步提升問題的質量, 加深理解. 1.1 Seismic exploration 問 DeepSeek (下同): 為什么進行地震勘探? 問: 地震勘探一般的深度是多少? 1.2 Sesmi…

mac電腦pytest生成測試報告

時隔了好久再寫代碼&#xff0c;感覺我之前的積累都白費了&#xff0c;全部忘記了&#xff0c;看來每一步都有記錄對于我來說才是最好的。 最近又要重新搞接口自動化&#xff0c;然而是在mac電腦&#xff0c;對于我長期使用windows的人來說真的是個考驗&#xff0c;對此次過程…

神經輻射場(NeRF)技術解析:3D重建與虛擬世界的未來

神經輻射場&#xff08;NeRF&#xff09;技術解析&#xff1a;3D重建與虛擬世界的未來 ——從算法突破到元宇宙基礎設施的演進之路 摘要 本文通過算法演進圖譜、訓練流程解析、PyTorch代碼實戰及產業應用洞察&#xff0c;構建從學術創新到工程落地的完整技術框架。實驗數據顯…

ES搜索知識

GET /categories/1/10?name手機 // 按名稱過濾 GET /categories/1/10?type電子產品 // 按類型過濾 GET /categories/1/10?name手機&type電子產品 // 組合過濾 查詢參數 ApiOperation(value "獲取商品分類分頁列表")GetMapping("{page}/{limit}")…

【Docker】Docker拉取部分常用中間件

一、拉取MySQL 這里以Docker拉取MySQL5.7為例 #拉取鏡像 docker pull mysql:5.7 docker run -d --name oj-mysql -p 3306:3306 -e "TZAsia/Shanghai" -e "MYSQL_ROOT_PASSWORD123456" mysql:5.7 -e 參數用于設置容器內的環境變量。TZ 是用于設置時區的環…

在 Ubuntu 上離線安裝 ClickHouse

在 Ubuntu 上離線安裝 ClickHouse 的步驟如下: 一.安裝驗證 # 檢查服務狀態 sudo systemctl status clickhouse-server #刪除默認文件 sudo rm /etc/clickhouse-server/users.d/default-password.xml # 使用客戶端連接 clickhouse-client --password