百度搜尋萬億規模特徵計算系統實踐

陶然陶然發表於2023-11-24

  本文主要介紹百度搜尋在全網萬億級規模內容做內容理解的工程實踐,涉及機器學習工程化、資源排程、儲存最佳化等多個Topic。

   01 業務背景

  百度收錄了網際網路海量內容,要索引這些內容,需要先對內容做深度理解,提取包括內容語義、內容質量、內容安全等多維度資訊,從而進一步支援內容篩選過濾、語義建庫等需求。對全網海量內容做深度理解,挑戰是非常大的,主要是體現在成本和效率上。

  在成本上,計算量非常大,除了因全網內容資料量大(萬億規模)、特徵數多外,有兩個趨勢也加劇了計算壓力,一方面是網際網路內容圖文化、影片化比例持續大幅增長,圖片/影片的計算量遠大於文字,另一方面,深度學習技術大規模應用,特別近期大模型的興起,對算力需求也隨之劇增。在效率上,怎麼讓系統更易用,儘可能地提升業務迭代效率,是所有工程系統的核心目標之一。  

   02 關鍵思路

  (1)成本最佳化:要滿足如此龐大的算力需求,需要極致地『開源節流』。

  1.『開源』:儘可能擴大計算資源池,透過採購來滿足ROI低,挖潛現有資源是關鍵。從公司整體看,資源使用並不充分,線上資源存在波峰波谷,庫存空閒資源也不少,而我們大多為離線計算,對資源穩定性要求不高,可以結合兩者,建設一套彈性計算排程系統來解決資源問題。

  2.『節流』:儘可能最佳化服務效能,降低單位計算成本,模型推理計算量大,但本身有較大的最佳化空間,結合模型結構和GPU硬體特點進行最佳化,可以大幅提升模型服務單卡吞吐。此外,最佳化CPU處理、使用百度自研崑崙晶片等多種方式也能降低單位成本。

  (2)效率最佳化:如圖所示,整體業務流程包括實時和離線計算兩部分,新增特徵需對存量資料離線刷一遍,而對Spider新收錄的資料,會篩選高時效性的資料實時計算,其餘的也離線計算,計算大頭在離線部分。效率問題主要為:怎麼支援模型快速工程化?怎麼提升離線計算效率?

  1.模型服務框架&平臺:模型工程化是透過統一的模型服務框架和配套的模型服務平臺來實現,模型服務框架和平臺支援並涵蓋從構建、測試、上線等模型服務全生命週期的各個環節。

  2.特徵批次計算平臺:為了離線特徵計算效率問題,建設了統一的批次計算平臺,分析並深度最佳化從離線任務開發到計算過程中各環節的效率和效能瓶頸,儘可能地提升效率。  

   03 技術方案

  3.1 整體架構

  整體架構如下圖所示,最核心的是模型服務平臺、批次計算平臺、計算排程系統、模型服務框架這幾部分。

  1.模型服務框架:演算法同學使用統一的模型服務框架進行服務封裝,基於研發效率考慮,選擇Python作為框架語言,但Python效能問題也很明顯,因此需要做很多針對性最佳化。此外,我們也在框架持續整合多種推理最佳化手段,儘可能地降低服務單位計算成本 。

  2.模型服務平臺:模型服務平臺支援模型服務DevOps和能力輸出,平臺以『運算元』作為管理粒度,『運算元』代表一種完整功能,如影片分類等,它通常需要多個模型服務組合使用。演算法同學在平臺註冊運算元,提供服務拓撲等元資訊,也透過自動效能調參、自動化壓測等生成效能報告,服務拓撲和效能報告是後續排程的重要輸入。平臺也提供運算元檢索、調研試用等功能,以中臺化方式支援其他業務需求。

  3.計算排程系統:計算排程系統做流量和資源的統一排程,所有對模型服務的請求都會經過計算排程系統的閘道器,執行流控和路由等流量策略,計算排程系統也會排程百度多個PaaS的多種空閒異構資源,自動化部署合適的運算元,給離線計算提供更大吞吐。

  4.批次計算平臺:批次計算平臺支援離線作業的任務生成、任務排程、DevOps等功能,建設基於HTAP的儲存方案,解決Scan吞吐瓶頸問題,並聯動計算排程系統,支援大規模離線計算。  

  3.2 技術關鍵點

  本章節主要闡述系統技術關鍵點,包括遇到的技術難點、思考和權衡折衷,一些共性問題也期望讀者能和我們多多交流。

  3.2.1 模型服務框架

  在實際業務場景,模型服務框架有幾個關鍵問題需要解決:業務程式設計模型、Python服務效能最佳化、以及推理效能最佳化,下面介紹。

  3.2.1.1 業務程式設計模型

  實現某個功能往往需要組合使用多個模型和多種資料處理邏輯,為了抽象表達處理流,實現通用邏輯複用,採用方案如下:

  將業務邏輯描述成DAG(有向無環圖),DAG上的節點稱為Op,DAG有多個Op組成,Op之間存在串聯和並聯關係,一個OP可以是模型推理或者一段處理邏輯,Op之間透過資料白板進行上下文傳遞。透過DAG能清晰地呈現整體處理流程,提升程式碼可讀性和可維護性。

  建設通用Op庫,像模型推理、影片抽幀、影片轉換等通用邏輯被整合成通用Op庫,支援業務複用。業務也可根據需要,定製擴充套件Op,並註冊到框架使用。  

  3.2.1.2 Python服務效能最佳化

  選擇Python降低了開發成本,但也引入了Python GIL(全域性直譯器鎖)問題,導致不能充分利用CPU多核,極大限制了服務吞吐,解決方案如下:

  採用多程式+非同步協程+CPU/GPU計算分離的併發方案,服務包含三類程式:RPC程式、DAG程式、模型程式,它們之間透過共享記憶體/視訊記憶體進行資料互動。

  PRC程式負責網路通訊,基於BRPC開發(開源版本:),我們最佳化了BRPC的Python實現,使其支援Python多程式和協程的併發模式,在實際業務場景測試下,最佳化後效能提升5倍+。

  DAG程式負責DAG執行(CPU處理),透過多DAG程式和Op執行非同步協程化來充分利用CPU多核。另一個比較重要的是ModelOp,它實際是推理代理(類似RPC),真正推理是在本地模型程式或者遠端服務執行,ModelOp遮蔽了呼叫細節,支援使用者方便地使用模型。

  模型程式負責模型推理(GPU處理),考慮視訊記憶體有限等原因,模型程式和DAG程式分離獨立,模型程式支援Pytorch、Paddle等多種推理引擎,並做了很多推理最佳化工作。由於Tensor資料通常較大,DAG和模型程式傳輸Tensor直接使用共享視訊記憶體,避免不必要的記憶體複製。  

  主要有推理排程、推理最佳化、模型量化、模型壓縮等最佳化手段,經過最佳化,服務單卡吞吐相比原生實現通常有數倍提升。

  1.推理排程:動態批次處理(DynamicBatching)和多Stream執行。GPU批次計算效率更高,由於服務也接受實時單條請求,沒法請求時拼Batch,因此採用服務內快取拼Batch,犧牲時延換吞吐。Stream可看做GPU任務佇列,預設全域性單條,任務序列執行,會出現GPU IO操作(記憶體視訊記憶體互拷)時,計算單元閒置,透過建立多Stream,不同推理請求走不同Stream讓IO和計算能充分並行。

  2.推理最佳化:業界主流方案是使用TensorRT,但是實際應用會有動態圖靜態化失敗、TensorRT Op覆蓋不全等問題。為解決這些問題,團隊自研Poros(開源版本:https://github.com/PaddlePaddle/FastDeploy/tree/develop/poros),結合TorchScript、圖最佳化、TensorRT、vLLM等技術,實現無需複雜模型轉化,新增幾行程式碼即可大幅提升推理效能,效率和效能雙贏,同時Poros也支援崑崙等異構硬體。

  3.模型量化:GPU、崑崙等硬體對低精度都有更強的算力,量化雖有少量效果損失,但帶來大幅吞吐提升,因此,上線都會採用FP16乃至INT8/INT4量化,這部分也是透過Poros支援。

  4.模型壓縮:透過模型蒸餾、模型裁剪等方法精簡模型引數,減少計算量,但是需要訓練,且效果有損,通常和演算法同學一起合作最佳化。  

  3.2.2 計算排程系統

  計算排程系統的執行架構圖如下,所有請求流量都透過統一的閘道器(FeatureGateway),閘道器支援流控、路由等多種流量策略。離線作業也透過閘道器提交計算需求,閘道器會將需求轉發給排程器(SmartScheduler)進行排程。排程器對接了百度內多個PaaS,不斷檢測空閒資源,根據需求、多種指標、空閒異構資源分佈等,自動化排程部署合適的運算元,運算元元資訊從服務平臺獲取,排程完成後,排程器會調整閘道器的流控和路由等。  

  系統比較關鍵的兩個問題:怎麼實現運算元(複合服務,含複雜服務拓撲)自動化部署?怎麼在流量分佈不穩定、多異構資源等複雜條件下進行排程?

  3.2.2.1 自動化部署

  為簡化排程器開發複雜度,採用宣告式程式設計,實際是基於k8s controller機制開發。運算元自動化部署實現方案如下:

  1.CRD擴充套件:利用K8S CRD來自定義ServiceBundle(運算元部署包)等物件,透過controller機制讓在PaaS等外部系統執行部署等操作。ServiceBundle包含了運算元需要的所有子服務部署資訊,以及其拓撲關係。排程建立運算元服務時,會從最底層開始逐層建立子服務,上層子服務可以透過通訊託管機制獲得下游子服務地址。

  2.通訊託管:通訊託管機制是基於配置中心和模型服務框架實現,服務啟動命令會帶有遠端配置地址和AppID,透過載入遠端配置可以實現下游服務地址啟動時變更。其實更理想方案是使用ServiceMesh等技術將架構能力和業務策略解耦,但考慮我們要在多PaaS部署,而在各個PaaS都部署ServiceMesh SideCar等元件成本較高,整合到框架又過於重,因此,先建設基於配置中心的方案,後續時機成熟再考慮遷移。  

  3.2.2.2 排程設計

  排程是個非常複雜的問題,在我們場景,其複雜性主要體現在以下幾方面:

  1.運算元排程:運算元(複合服務)可承載流量取決於其最短板的子服務容量,排程時需要整體考慮,避免長板服務資源浪費。

  2.流量分佈變化:部分運算元的效能會受輸入資料分佈影響,如影片OCR會受影片時長、畫面文字比例影響,排程時需要自適應調整。

  3.多異構硬體:運算元有些能支援多種異構硬體(崑崙/GPU/CPU等),有些只能繫結一種,怎麼分配才能保證全域性資源最有效利用。

  4.其他因素:作業優先順序、資源優先順序、資源波動等因素也都會影響排程,實際排程要考慮的因素非常多元化。

  基於以上因素考慮,我們的排程設計方案如下:

  1.兩階段排程:分流量排程和資源排程兩階段,各自獨立排程。流量排程負責對當前運算元服務容量分配到各個作業,並結果同步到閘道器,調整流量策略;資源排程負責根據資源空閒情況和運算元容量缺口等進行排程,最終對運算元服務例項進行擴縮容。

  2.流量排程:流量排程Adjust階段會根據任務執行指標等調整歸一化係數,再用係數將任務所需Qps對映成NormalizedQps,NormalizedQps是後續所有排程的依據,從而解決流量分佈變化影響問題。在Sort階段會根據作業優先順序等排序,在Assign階段會根據Sort結果,按優先順序將現有運算元容量分配到各個作業。Bind階段會將結果執行,同步路由等到閘道器。

  3.資源排程:資源排程Prepare階段會先將作業的容量缺口轉換成對應服務例項數缺口;接著進行HardwareFit,將要擴容的服務分配到合適的硬體資源佇列,並根據資源稀缺性、計算價效比等進行Sort;然後進行PreAssign,對各子服務進行資源預分配,最後GroupAssign階段考慮複合服務的各子服務排程滿足度,對複合服務的各子服務容量進行細調,避免資源浪費。  

  3.2.3 批次計算平臺

  批次計算平臺要解決的問題:彈性資源比較充裕時(如夜間),對Table(分散式表格系統)的Scan吞吐瓶頸,以及怎麼儘可能地最佳化離線任務效率,下面介紹具體解決方案。

  3.2.3.1 HTAP儲存設計

  先分析對Table Scan慢的原因,主要如下:

  1.讀寫混合:OLTP(抓取更新等)和OLAP(特徵批次計算等)需求都訪問Table,多種讀寫方式混合,而底層採用HDD儲存,大量讀寫混合使磁碟IO吞吐嚴重下滑

  2.Scan放大:Table採用寬表結構儲存,不同任務Scan時通常只需要其中的某幾列,但Table Scan時需要讀取整行資料再過濾,IO放大嚴重。

  3.擴容成本高:由於OLTP和OLAP混合讀寫,要為Scan單獨擴容成本高,同時因讀寫比例難以固定,也很難預估擴容資源。  

  透過上述分析可知,關鍵問題還是OLTP/OLAP混合使用Table。參考業界實踐,採用單一儲存引擎難以同時滿足OLTP和OLAP場景,但為了儲存系統易用性,又希望一套儲存系統同時支援兩種場景。因此,我們結合業務場景和業界經驗,實現一個HTAP儲存方案,具體方案如下:

  1.OLAP/OLTP儲存分離:針對批次計算等OLAP場景建設高效OLAP儲存,減少因OLAP/OLTP混合使用Table帶來的讀寫混合問題,也可根據需求單獨擴容。

  2.高效OLAP儲存設計:自研OLAP儲存基於Rocksdb、AFS(百度類HDFS)構建,採用增量同步、行資料分割槽、列資料動態合併儲存的設計,將Table全量資料劃分成N個資料物理分割槽,利用Table的增量Snapshot定期高效同步更新OLAP儲存資料(由於Table底層採用LSM儲存,增量Snapshot效率遠高於全量Scan)。列儲存根據欄位訪問熱點重新組織,將熱點列在物理層一起儲存,降低IO放大,也支援動態調整。方案會存在資料同步延時問題,但在我們場景,時效性要求不高,問題可以忽略。

  3.HTAP SDK:提供統一的SDK同時支援對Table和OLAP儲存訪問,使用者基於SDK可以同時執行自己的OLAP和OLTP任務。  

  3.2.3.2 任務生成與排程

  為了簡化批次計算任務的開發,平臺目前提供了三種任務開發模式:配置化、KQL、離線框架,開發自由度/成本由低到高,易用性由高到低:

  配置化:針對通用並頻繁使用的任務型別,平臺對這些任務進行高度封裝,只需要在Web介面上配置即可生成任務。

  KQL:KQL是自研的類SQL語言,提供多種通用函式,並支援自定義函式(類似Spark UDF),使用者可以透過KQL查詢和處理資料。

  Function classify = {def classify(cbytes, ids):unique_ids=set(ids)classify=int.from_bytes(cbytes, byteorder='little', signed=False)while classify != 0:tmp = classify & 0xFFif tmp in unique_ids:return Trueclassify = classify >> 8return False}declare ids = [2, 8];select * from my_tableconvert by json outlet by row filter by function@classify(@cf0:types, @ids);

  離線框架:框架提供包括資料讀寫、通用轉換等功能,使用者按照框架規範自定義邏輯並生成離線任務部署包提交平臺,平臺進行任務排程。

  除了以下幾種方式,平臺也在嘗試結合大模型實現基於自然語言的任務生成。實際上,無論採用哪種方式,最後生成的離線任務都是基於離線框架,只是根據更具體的場景提供了更高度的封裝而已。

  任務生成後,會將任務排程到MapReduce或者FaaS平臺執行,不同任務生成方式在排程前的預處理有所不同,比如KQL任務需要先做KQL解析再生成實際任務做排程,而業務透過框架開發的任務比較容易出現各種非預期問題,所以走自動化准入等DevOps流程。任務執行時,會先向計算排程系統提交需要的運算元以及期望吞吐,之後不斷向閘道器獲取要可用Quota,並結合當前任務例項數、失敗率等,自適應調整請求投遞速度。  

   04 總結

  當前系統支援搜尋出圖、影片搜尋、圖片搜尋等十多個業務方向,支援數百個運算元的研發和上線,天級數百億的計算呼叫,支援全網萬億規模內容特徵的例行更新。隨著AI大模型時代的到來,帶來很多新的場景和挑戰,有很多點值得重新思考,後續我們將結合大模型進行更多的探索。

來自 “ 百度Geek說 ”, 原文作者:百度Geek說;原文連結:https://server.it168.com/a2023/1120/6830/000006830334.shtml,如有侵權,請聯絡管理員刪除。

相關文章