![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f6a70672f6c426841453432774b576f5373414167685a4769624d396f544c4d33346f37645349746a3442576f7369634454586f6551473077745666355938385533655967756b7a6b4f4848344644516d566d774e786a79346d4c70412f3634303f77785f666d743d6a706567.webp)
作者 | 袁進輝
上周寫了一篇《淺談GPU虛擬化與分布式深度學習框架的異同》,想不到引起很多關注和討論。和朋友們討論之後,覺得這個話題值得再發散一下:
首先,文章只討論了GPU「一分多」這種「狹義」的虛擬化,還存在另外的虛擬化,「多虛一」也是存在的。在此,我想特別強調的是:上一篇文章後半部分對深度學習框架「多合一」的討論恰恰是想說明「多虛一」是不可能靠僅僅一層包打天下的API就能實現,反而要靠」去虛擬化「的思路。
其次,「去虛擬化」這種理念在追求性能極致的場景不是新鮮事,在這篇文章里再舉幾個相關的例子。
在這篇小品文里再討論一下:虛擬化的本質和局限性,為什麼在追求性能極致的場景要「去虛擬化」。
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f4649425a6563377563436842376a50464b6962657a6a5a586a744a314745486644423577354e5779656c4c76354d786962766e55484c63434a494353716c70565a4257546a4b654462654d4c30624a70376d41504f4951772f3634303f77785f666d743d706e67.webp)
「虛擬化」可能是計算機科學歷史上最偉大的思想之一,對此,計算機先驅David Wheeler有一句名言:
All problems in computer science can be solved by another level of indirection, except for the problem of too many layers of indirection.
這句話被尊稱為「軟件工程的基本定理」,並被C++之父Bjarne Stroustrup在專著《The C++ Programming Language》的序言處引用。不過,大部分人記住了前半句,卻忽略了後半句,而後半句正是本文想展開討論的。
"another level of indirection"刻畫了「虛擬化」的精髓:通過引入一層新的抽象,把與上層應用無關的細節隱藏掉,有選擇的給上層用戶暴露一些功能供其使用,也稱底層細節對用戶透明,既不損害上層應用的功能,又能享受「關注點分離」(separation of concerns)的好處,增加易用性。
可以說:虛擬化的案例無處不在,無往而不利。
操作系統是對硬件資源的虛擬化:計算核心被虛擬化成進程;硬件內存變成虛擬內存;存儲介質被虛擬化成文件系統;網絡傳輸通過多層協議棧被虛擬化成文件描述符,使得數據傳輸就像讀寫普通的文件一樣。
分布式存儲把通過網絡互聯多個單機文件系統虛擬化成的一個網絡文件系統,使得用戶不用再關心網絡數據傳輸的細節,可以像訪問本地文件一樣訪問其它節點上的數據。
當然,虛擬化造就了今天偉大的雲計算技術和市場,雲原生如火如荼,開發者只需要基於雲服務的API編程,而不需要關於API 之下的物理細節,成本低,可靠性又高。
虛擬化的成功不可否認。但是,成也蕭何,敗也蕭何,虛擬化也有其代價。
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f4649425a6563377563436842376a50464b6962657a6a5a586a744a314745486644423577354e5779656c4c76354d786962766e55484c63434a494353716c70565a4257546a4b654462654d4c30624a70376d41504f4951772f3634303f77785f666d743d706e67.webp)
一層層的抽象層次,一步步降低編程的複雜度,每一層都向上隱藏了一些東西,上層就相應地丟失一些操控能力。每經過一層抽象,就引入一些對上層而言的「不確定性」,最終的結果是,性能的天花板一步一步下降。
以Hadoop的分布式存儲為例,虛擬化的結果是掩蓋了數據真實的存儲位置,不管在哪個節點上,訪問接口都是一樣的。不過,當向這個集群調度一些MapReduce的數據處理任務時,如果Task被調度到數據所在的節點上,那麼就不需要網絡傳輸,如果調度到另外的節點,就需要把數據讀到內存並通過網絡發送到計算所在的節點。也就是,被隱藏掉的數據位置信息有可能被調度器用來提升系統效率。
隨着摩爾定律放緩,不止在深度學習領域,在任何追求極致性能的場景就出現了一種擊穿、擊碎中間抽象層次,一竿子插到底進行協同優化的強烈需求。
讓我們看六個例子。
1、操作系統把CPU資源抽象成被操作系統調度和管理的內核線程,避免了用戶調度計算資源的麻煩,但是,在高並發場景,用戶級線程(如coroutine)越來越多,計算資源更多的在用戶態(user mode)來調度,而不是完全依靠內核的調度。
2、操作系統通過頁表機制實現虛擬內存,神不知鬼不覺實現數據的換入換出,但在高性能場景,用戶程序會通過編程接口禁止這一行為,譬如在RDMA和GPU異步數據傳輸都依賴於鎖頁內存,確保操作系統不會幫倒忙。
3、傳統的網絡傳輸協議棧TCP/IP都在操作系統內核,為了避免內核態和用戶態的數據拷貝和上下文切換,人們先是使用用戶態協議棧(如Intel dpdk),仍無法滿足需求的話,就使用支持內核旁路(bypass)的RDMA技術,完全跳過了操作系統這一層。
4、緩存(Cache)技術也可以視為一種虛擬化,命中就直接使用,沒命中就花時間從更慢的存儲取過來,在局部性較好的負載下工作得很好。但在特定領域,譬如深度學習領域,工作負載有特別的規律,如果用軟件來控制緩存數據的彈出以及讀取甚至可以做到100%的命中率,這種辦法被稱為Scratchpad,現在幾乎所有的AI芯片都沒有用Cache,而是使用Scratchpad技術。
5、操作系統的文件系統或者網絡文件系統,把底層硬件的細節隱藏了,編程時不需要考慮數據在哪台機器的哪塊磁盤上,編程更簡單了,但訪問近處的數據速度快,訪問遠處的數據速度慢,為了提高效率,有人就提出了locality aware(局部性感知)的調度,把計算任務儘可能調度到存儲數據的節點上去。
6、工業級的大型軟件系統里,通常不會使用庫函數,恨不得把底層代碼庫重新打造一遍,譬如Chromium, OceanBase等大型C++項目,我想,這也可以算作反虛擬化的例子。
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f4649425a6563377563436842376a50464b6962657a6a5a586a744a314745486644423577354e5779656c4c76354d786962766e55484c63434a494353716c70565a4257546a4b654462654d4c30624a70376d41504f4951772f3634303f77785f666d743d706e67.webp)
虛擬化試圖向上層提供一劑一勞永逸的靈丹妙藥,既解決易用性,又不損害上層應用對性能的需求。但是,「虛擬化」到底好不好應該具體問題具體分析,那該如何判斷虛擬化到底合不合適呢?
這裡嘗試拋出一個用來判斷是否有必要引入一層抽象(虛擬化)的量化指標,供參考。每引入一層虛擬化,就向上層隱藏了一些東西,向上提供的服務的延遲就因此引入了不確定性(以分布式存儲為例,有的數據近,有的數據遠;以分布式GPU資源池為例,有的近,有的遠),服務的響應延遲有一個波動範圍,也就是[min, max],延遲越小越好。
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f6c426841453432774b57715a5053696248446b726963325069617a424d466e5a5172487a6b4a6b576554577a6b5169627254305849515138313579556637466963445152357132775243647a62696368484e61345746566b305772672f3634303f77785f666d743d706e67.webp)
如果上層某一個應用需要保證的最低延遲仍大於max,那麼這層虛擬化對這個應用就沒有損害,可以大膽的引入這個虛擬化技術(也就是上圖的C區域)。
如果上層某一個應用需要保證的最低延遲介於min和max之間,那麼引入這層虛擬化就對這個應用是有損害的,把這層虛擬化敲掉,就可以確保以min值滿足這個應用對延遲的需求(也就是上圖的B區域)。
如果上層某一個應用需要保證的最低延遲小於min,那麼即使敲掉當前層的虛擬化,仍不能滿足這個應用,就需要繼續向下敲,進行聯合優化,直到延遲得到滿足(也就是上圖的A區域)。
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f4649425a6563377563436842376a50464b6962657a6a5a586a744a314745486644423577354e5779656c4c76354d786962766e55484c63434a494353716c70565a4257546a4b654462654d4c30624a70376d41504f4951772f3634303f77785f666d743d706e67.webp)
如上所述,虛擬化的基本思路是通過「隱藏細節」給上層應用提供一種假象,降低上層應用使用底層資源的複雜度。不過,有時候,「隱藏」掉的信息會阻礙上層應用挖掘極致的性能。有沒有兩全其美的辦法呢?
David Patterson在《計算機體系結構的黃金時代》一文中開出的藥方是從算法到硬件直接打通,結合算法和硬件的特點全部搞定製。定製表現在DSL和DSA,一方面是設計領域特定語言,方便編程,另一方面為面向領域應用設計領域特定架構,挖掘極致效率。
David Patterson的藥方既不是在已有方案中插入新的抽象層次,也不是完全不要中間抽象,而是把原有的抽象層次全部敲碎重建,這種重建是基於對算法和硬件特點的充分挖掘。我理解這裡有倆關鍵:軟硬件協同設計(分工),以及編譯器技術。
一方面,這裡的思路和虛擬化不同之處是:不是向上層應用隱藏什麼,而是強調要向上層暴露什麼,或者說向上層讓渡什麼職責。
另一方面,這裡沒有期待只要提供一層API的抽象就包打天下,而是對從算法到硬件的映射複雜性有充足的認識。這種映射既包含任務相關但硬件無關的問題,也包含硬件相關的問題,本質等同於人們熟知的編譯器技術。
說到這裡,想到另一個「去虛擬化」的絕佳例子,就是軟件定義網絡(Software defined network,SDN)。對於同一套硬件基礎設施,承接不同的工作負載,交換機的最優轉發規則是不同的。SDN的思路是,令交換機的轉發規則是可編程的,對每一個不同的業務負載,都用靜態分析得到最優的轉發規則或策略(control plane),並按照這個規則對交換機編程,交換機在轉發數據時只需要按照已編程的規則執行即可(data plane)。
在SDN的例子裡,交換機讓渡了一部分職責給軟件,軟件根據不同的業務負載都生成不一樣的路由規則(策略),這是SDN平衡靈活性和極致性能的關鍵。
在OneFlow解決分布式深度學習的難題時,也是類似的思路,它不是靠額外引入的一個抽象層次實現,而是分成了控制平面和數據平面。在控制平面,編譯器根據特定深度學習模型的任務負載和底層硬件拓撲生成對這個配置最優的執行計劃(execution plan), 這個plan不是一勞永逸的,它需要上層模型的知識,也需要底層硬件的知識,模型或硬件拓撲一旦變化,plan就會變化。但是,編譯器生成plan的機制可以認為是不變的,也是整套系統的精髓。
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f4649425a6563377563436842376a50464b6962657a6a5a586a744a314745486644423577354e5779656c4c76354d786962766e55484c63434a494353716c70565a4257546a4b654462654d4c30624a70376d41504f4951772f3634303f77785f666d743d706e67.webp)
這篇文章補充解釋了我對「通過向上層算法隱藏硬件信息」的虛擬化思路,以及「通過向上層算法暴露硬件信息和讓渡職責」的去虛擬化的思路的理解。
簡單來說,虛擬化的思想強調的是隱藏(底層細節)和限制(上層的功能範圍),潛台詞是:我認為對上層應用的需求足夠了解,告訴上層應用太多細節也沒有用,我把底層的東西一攬子處理好了,你只管調用我為你提供的API就可以了。
軟件定義的思想強調的是暴露(底層細節)和讓渡(硬件的策略可被軟件分析和配置),潛台詞是:我對上層應用的需求了解不足,沒有辦法為上層應用提供一個足夠令人滿意的一攬子的策略,於是乾脆把底層策略暴露給上層,上層應用對自己的需求和任務負載最清楚,上層自己來負責生成策略並來配置底層。
致力於提升深度學習系統性能的朋友對「去虛擬化」的思路應該是習以為常的,這應該是整個社區經過探索和碰壁逐漸收斂出來的主流思路,不是我個人的發明,我只是把觀察和理解寫下來而已。
順着這個思路,算法-軟件-硬件協同優化領域正在發生一些令人興奮的進展。毫無疑問,廣義上的編譯器技術(無論是單設備代碼生成,還是分布式執行計劃生成)是裡面最核心的部分。
註:題圖源自Pixabay
百度大腦OCR 技術加持白描 App
一文全覽機器學習建模流程
使用Mask-RCNN的停車位檢測
波士頓動力機器人解鎖跑酷技能
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f56654a4b5849747077505832465a56784a6262455239387269614b786662646963416f61576962696268564869634d6854306c4a5238587658733655316671416e6d68483271316f6f496d724167534956664e69636a4e4c4d7a5a512f3634303f77785f666d743d706e67.webp)
分享
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f56654a4b5849747077505832465a56784a6262455239387269614b786662646963414c38626b4969614c49716e6e304f593657313054526774534d574c4d4f7869636d3371326962624a38593979624a4273696171655471427071512f3634303f77785f666d743d706e67.webp)
點收藏
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f56654a4b5849747077505832465a56784a6262455239387269614b7866626469634143744271714f6766387144696378584a5941796e6d67426a67716146595a6348355077596a6962496365594c765843363646416678544f512f3634303f77785f666d743d706e67.webp)
點點讚
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f56654a4b5849747077505832465a56784a6262455239387269614b78666264696341324e344945614a696339687a4135566c6b5a377a794438456962653567525835696165355a4836625266373271447a3676316e6e42457078772f3634303f77785f666d743d706e67.webp)
點在看