1. 程式人生 > >讓我們致力於一個LLVM超級優化器

讓我們致力於一個LLVM超級優化器

作者:JohnRegehr, Professor of Computer Science, University of Utah, USA

編譯器優化有兩個基本部分。首先,它需要識別IR(中間表示)中的一個可優化情形,比如同一個變數的鄰近遞增。要使優化效能最大化,情形識別器應該儘可能地廣撒網。比如,注意到它可以處理同一基本塊裡由無關指令隔開的遞增,而且也可以處理遞減,我們可以拓寬一個遞增合併器的適用性。優化的第二部分是,一旦找到可優化的程式碼,知道用哪些程式碼替換它。在這個例子裡,“i++;i++”變成“i+= 2”。雖然某些優化比這個要複雜得多,基本的思想都是適用的。優化編譯器通過持續應用大量的優化遍,每個遍以某種輕量級的方式改進程式碼,取得良好的結果。

讓我們看一個更有趣的例子。考慮這個C函式:

int cannot_overflow (int a)

{

  if (a==INT_MAX) return 0;

  return a+1;

}

現在使用Clang的未定義行為消殺器構建它:

clang -S -emit-llvm -O2 cannot_overflow.c-fsanitize=undefined

下面是我們希望看到的輸出(IR層級):


換而言之,我們希望看到的程式碼實現了這個函式,但沒有額外的UBSan(UndefinedBehavior Sanitizer)邏輯——不需要溢位檢查,因為不管實參值是什麼,這個函式顯然不會執行一個未定義的溢位(實際上,因為跳轉的消除,LLVM程式碼可以執行一個未定義的溢位。這沒有問題的,因為LLVM中的一個未定義溢位僅影響操作的結果,相比之下,C層面未定義行為的後果難以預料)。

上面的程式碼正是我們希望的。我們實際得到的——使用截止今天的Clang/LLVM,也包括Clang/LLVM3.4——是這個:


以上面的兩部分模型刻畫LLVM缺失的優化,可優化的情形是“一條帶有溢位的加指令可以顯示出從不觸發溢位的行為”,替代的程式碼是“普通的加指令” (對溢位處理控制代碼呼叫的去除是另一個優化,其情形是“顯示出不會執行的程式碼”,其替換程式碼是“nothing”)。

我們可以下結論說LLVM在優化整數表示式時不是非常聰明嗎?不,事實上當談及編譯器優化時,這個型別的生成是很少有用的——魔鬼在細節中。例如,讓我們嘗試該溢位檢查的一個開源版本:

static int safe_int_add (int a, int b)

{

  assert (!(b > 0 && a > INT_MAX-b));

  assert (!(b < 0 && a <INT_MIN-b));

  return a+b;

}

int cannot_overflow_2 (int a)

{

  if (a==INT_MAX) return 0;

  return safe_int_add (a, 1);

}

在這個情形裡,LLVM生成理想的輸出。另一方面,如果我們以一個微不足道的方式修改這個程式碼,LLVM不能優化掉這些斷言:

int cannot_overflow_3 (int a)

{

  if (a==INT_MAX) return 0;

  return safe_int_add (1, a);

}

稍作調整,讓我們快速看一下GCC4.8.1。它不能刪除由它的-ftrapv邏輯插入的整數溢位檢查。另一方面,它能夠完全優化cannot_overflow_2()與cannot_overflow_3()。

啥意思?要點是製作一個聰明的編譯器是經年累月教它優化的過程。這個教導過程是非常精細的。例如,我們只是看到LLVM可以對cannot_overflow_2()釋出良好的程式碼,但對cannot_overflow_3()不能。要修正這,需要修改一個或更多的LLVM遍,一個要求智慧、經驗、高工資,且可能超量工作的編譯器工程師參予的易錯過程。要優化原來的cannot_overflow()將需要完全不同的一組改變。如果有一個更好的方式使得編譯器更聰明,解放我們的工程師去實現C++14或其他大小,不是很好嗎?

超級優化器

不是依賴人類,一個超級優化器使用基於搜尋的技術查詢簡短的,近乎最優的程式碼序列。自1987年最早的論文,已經發表了幾篇論文;我喜歡的一篇是2006年的窺孔超級優化器。多年來,我認為LLVM可以從工作在IR上的一個窺孔超級優化器中獲益。

稍後我覺得可能是時候著手這個專案了。推動我行動的是Xi Wang的一系列觀察,他優秀的Stack工具已經知道如何將LLVM轉換為一個SMT例項,使得一個解析器可以回答關於LLVM程式碼的問題。為了實現超級優化器,我們需要解析器證明在一個程式中發現的某個LLVMIR與我們期望替換它的更小、更快的IR片段是等效的。正如你在這個檔案中看到的,LLVMIR與現代位向量感知的SMT解析器之間的語義差別是細小的。

在進入細節之前,我們需要看一下超優化LLVMIR的這項傑出工作,它包含了之前超級優化器裡(就我所知)沒有見過的兩個想法。首先,它工作在指令的一個DAG上,而不是線性序列。其次,它利用了未定義行為的知識。另一方面,這項工作有幾個需要修正的侷限。首先,它不總是能提出正確的轉換。這個問題通過SMT解析器相對容易解決。其次,其目的是向人類提供思路,然後人類向一個編譯器遍,比如指令合併器,新增某些模式。雖然在短期手工實現是務實的,我猜它是乏味、易錯且不必要,尤其是隨著LLVM程式碼替換程式碼大小的增長。

雖然我自己正在撰寫這篇部落格,這裡的許多材料來自與XiWang及DuncanSands的對話。同時他們兩人對本文的草稿也給出了反饋。實際上Duncan給了我如此多的反饋,我不得不重寫了幾個章節。

LLVM超級優化器的設計梗概

我們已經看到編譯器優化需要識別可優化的程式碼,並以更好但功能相同的程式碼替換它。第三個要求是它能極快地完成這些事情。讓我們看一下對LLVM這如何奏效。

為了找出優化的候選者,我們將以一個相當大的優化的LLVMIR採集開始,收穫不超過某個尺寸的每個合適指令的DAG。一開始,僅ALU指令適合超優化,但我期望我們可以不太麻煩地處理記憶體操作,區域性控制流以及向量。浮點程式碼與迴圈更具挑戰性。這裡是LLVM IR的文件,如果最近你還沒有看過。

為了抑制僅在無關細節上不同的DAG,我們需要一個規範化遍。然後,對於每個規範的DAG,我們的目標是找出一個功能相同,代價更低的DAG。這個代價高昂;我們將不僅需要離線執行它,而且我們還可能希望在一個叢集裡展開工作。方法是窮舉LLVM程式碼的規範DAG,直到比我們在收穫遍中最大尺寸稍小的尺寸。接著我們需要回答這個問題:哪個被列舉的DAG在功能上等同於我們收穫的DAG?來自2006年超優化器的技術是使用幾個測試向量快速地篩選大部分不等效的程式碼序列,然後使用一個解析器徹底地去除餘下的不等效序列。這裡將使用相同的想法。

到目前為止,我們花費了大量的CPU時間查詢可以被更廉價指令替換的LLVM指令的DAG。下一個問題是使得這些轉換在編譯時刻可用,且不會太過降低編譯速度。我們將需要快速地識別在正在優化檔案裡合格的DAG,規範化它們,以及重寫它們。某種型別的雜湊方案看起來是條路子。Duncan建議我們可以使用超級優化器本身來生成一個快速的規範化器——這很酷。

總之,我們擁有超級優化器本身,它是慢速的。大多數人不會想執行它,但擁有大量計算資源的大型組織可能想這麼做。我們還有一個使用預先計算的優化的LLVM遍,它自動生成,並且由一個SMT解析器證明正確性。

改進

下面是某些推測性更多的想法。

利用資料流實際情況。讓我最為激動,想要嘗試的事情之一是,在一些資料流實際情形的上下文裡,比如“進入的變數x是4的一個非負倍數”以及“進入的指標p與q不互為別名”,優化每個DAG。我視這為窺孔超級優化器的主要缺點之一——目光短淺地一次僅考慮少量程式碼,的一個可靠變通方案。資料流實際情況將支援遠處的某個活動。雖然在一個DAG輸入上的限制——“進入的變數z是奇數”或無論什麼——看起來最為明瞭,我相信我們還可以利用輸出值上的反限制(anti-constraint),比如“輸出的變數w無關緊要,除非它在範圍[0..9]。” 例如,如果準備把w用作一個包含10個值的陣列的索引時,後者就會發生。不言而喻,未定義行為的這種邪惡的使用應該是可選的,僅在與基於UBSan的測試結合使用時才是合理的。

在優化時刻將會發生的事情是,在要優化程式碼中,我們將擁有一個DAG,它的形狀匹配來自優化資料庫的一個DAG,不過相關的資料流資訊沒有完全匹配。我相信這容易處理:如果要優化程式碼中的資料流資訊比優化資料庫中的資料流資訊更嚴格,我們可以執行替換。例如,如果我們有一個DAG,其中進入值x> 10,我們可以應用一個從更弱條件x> 0匯出的優化,但不是更強的條件x> 20。

利用資料流實際情況的問題是,它使得超級優化器的每個部分變得更復雜。我們需要仔細考慮什麼型別的資料流資訊會實際向超級優化器實施好的影響。我已經讀過像值追蹤這樣的遍,它看起來提供了相當多我們希望的東西。

避免DAG列舉步驟。列舉並測試候選的DAG是不優雅的。避免這不是很好嗎?超級優化器DenaliDenali-2就是基於一個SAT解析器可以用於直接合成匹配一個規則的最優指令序列的想法。論文沒有提供許多反映這個技術奏效的線索,但如果Denali走在時代前面將會怎麼樣?SAT/SMT解析器的改進,以及過去10年間處理能力的提升可能使得這個技術,或密切相關的技術,成為一個現實的提議。

比列舉更傾向合成的另一個原因是,基於列舉的一個做法對常量沒有太好的辦法:有太多的選擇。這意味著我們的超級優化器不能發現令人驚奇的技巧,像快速反向平方根(fastinverse square root,它是一個近似法,因此它會在等效性測試中失敗,不過你明白這個思想)。

更好的代價函式。估計LLVM指令的一個DAG的執行代價並不簡單,因為——儘管這樣叫——IR是相當高階的(它不像估計真實指令的執行代價在現代處理器上那麼容易)。我們可以想象以簡單、黑客式的方式估計代價,比如計算指令個數或度量DAG的最長路徑。另一方面,把一些事情做得更精確可能是值得的,比如將某些DAG編譯為機器程式碼,在某些測試向量上測量它們的執行代價,然後使用結果資料來匹配一個代價模型。可插拔代價函式將使得優化程式碼大小而不是速度變得容易——目前編譯器還不特別擅長的事情。

Duncan猜想“指令數”將是我們需要的唯一的代價函式,但我不確定。我們真的希望超級優化器用一個偏轉和相加替換一個乘法嗎?

優化整個迴圈。雖然任何超級優化器可以優化迴圈內程式碼,我不知道任何旨在優化包含迴圈程式碼的超級優化器。看起來有兩個阻礙。首先,搜尋空間可能變得成問題地大。其次,在SMT中編碼有關迴圈——展開它幾次的簡單做法——是不牢靠的。我可以使用一個SMT解析器來證明兩個迭代次數未知的迴圈的等效性嗎?我感覺我已經讀過討論這如何做的論文,但我現在找不到它們——誰能提供引用,十分感謝。

生成優化。Duncan的超級優化器在缺少LLVM型別資訊的情形下工作,因此它可以推匯出一個允許我們,比如以-x替換y-(x+y),而不管x與y的寬度。我列出的超級優化器將遭遇一點規則爆炸,因為它將對每個寬度匯出一個獨立的優化。儘管我看過的LLVM程式碼由具有明顯寬度(1,8,16,32,64,128)的值支配,當我們嘗試考慮資料流資訊時,規則爆炸可能成為一個嚴重的問題。一個有趣的研究片段將是使一個超級優化器自己概括優化。這可能通過在推導每個優化時概括它們來完成,或者事後在匯出優化的資料庫上運行同一個概括遍,查詢可以由一個更廣泛的規則歸納一組優化來完成。總之,如果我們在可能時生成廣泛可用的優化,而在必要時生成精細可用的優化,我們優化器的效能與功效將是最好的。

與LLVM後端整合。一個IR超級優化器總是會錯失利用在LLVM層面不可見硬體特徵的優化,例如,因為LLVM沒有rotate指令,這裡討論的優化型別遙不可及。這是一個可接受的權衡,但在另一方面,開發古怪指令的語義細節是我們期望超級優化器擅長的一個領域。我們可以擴充套件我們的LLVM->LLVM超級優化器,使得它除了優化IR,還可以輔助後端任務?或者那就是一個完全獨立超級優化器的工作?我還不知道答案,但我確實知道一個純粹的post-pass超級優化器(2006窺孔超級優化器就是這樣工具的一個例子)是有問題的,因為使它感知併發模型將是困難的。換而言之,它將嘗試優化掉用於與其他執行緒,硬體裝置等通訊的記憶體引用。

結果

如果這能成功工作,我們期待什麼樣的結果?

真實程式碼上新穎的優化。你可以斷定我不是一個真正的編譯器人,因為我不在乎使SPEC更快。我只關心那堆塵封的廢物以最快的速度執行。另一方面,從LLVM目前尚不能完全優化的Emscripten與GHC產生出幾乎肯定有趣的慣用程式碼。類似的,正如我在文章開頭顯示的,當我們在Clang中開啟整數溢位檢測,生成的程式碼有大量改進的空間。超級優化的聖盃是自動發現並應用像我們在Hacker’s Delight裡讀到的技巧。

未定義的程式崩潰。不久之前關於未定義行為的積極利用我寫了一個小小的建議。要說明一點,我不完全是認真的。好吧,猜猜怎麼著?一個UB感知的超級優化器將或多或少以我描述的方式粉碎未定義的程式。顯然,我對這個有複雜的感情,但我相信只要被超級優化器利用的每個未定義行為有一個對應的UBSan檢查,這可能就是一個合理的權衡。

歸類現存編譯器遍。理想地,我們將一個超級優化器指向未優化的LLVM程式碼,一旦到達一個定點,程式碼至少與LLVM優化器可以產生的一樣好。我不期望這會發生,但在另一方面2006超級優化器能夠接受一個未優化的x86小函式,並把它轉換為gcc-o2輸出的對等物。這裡更有意思的可能性是編譯器內部,幾十年來它變得越來越複雜,對某些類別的轉換,依賴於超級優化器,可以變得更簡單。我認為我們可以指望替換部分的指令合併器,在Csmith指向LLVM後,我們發現它是錯誤程式碼bug最豐富的源泉之一。

我們為更雄心勃勃的工具鋪平了道路。剛才,我寫到CompCert需要一個超級優化器。這仍然是真的。我不準備致力於它,但我期望從一個超級優化器得到的技術與教訓將有助於我們建立其他超級優化器。

結論

通過提議缺少的優化,超級優化器的結果已經滲透入LLVM與GCC。我認為這個技術是時候承擔更直接的角色。一個現實的超級優化器,一旦啟動,將自然地吸收在SMT解析以及計算能力中的進步。例如,如果今年我們可以收割大小直到10的IR DAG,明年這可能是12。如果實現這個想法,它將留給編譯器開發者更多時間關注其他任務。

3月4的更新:

·        本文的標題說“讓我們致力於”,但後續解釋這的章節在某個修改中迷失了。我的意思是我認為以一個開源的形式致力於這個專案是一個好的想法。我的團隊在作為開源工具的Csmith與C-Reduce上有很好的經驗。方程中使事情複雜化的部分是作為一個開發者,我將需要基於這個工作發表論文。我相信正確的答案是,給予對論文中描述的工作有所貢獻的人成為論文合作者的機會。

·        昨天在ASPLOS,我與SantoshNagarakatte好好談了一次;他的VerifiedLLVM工作與本文相關,使這些專案以某種方式協同工作將是很酷的。

·        我還與Vikram Adve談過;他的直覺是一個超級優化器對後端的貢獻要超過對中間端。可能正是如此,但讓我們審視一下一個中間端超級優化器的基本原理。首先,在後端產生死程式碼是錯誤的,超級優化器有時會這樣做——連同產生希望由其他遍進一步優化的,其他型別的程式碼。其次,僅在IR上操作,我們得到了各種簡化,使得進行更雄心勃勃的專案更容易,比如利用資料流資訊,合成最優IR等。正如我在上面說的,一個後端超級優化器也是肯定值得要的。

感謝所有出色的評論。


相關推薦

我們致力一個LLVM超級優化

作者:JohnRegehr, Professor of Computer Science, University of Utah, USA 編譯器優化有兩個基本部分。首先,它需要識別IR(中間表示)中的一個可優化情形,比如同一個變數的鄰近遞增。要使優化效能最大化,情形識別

.NET Core微服務之路:我們對上一個Demo通訊進行修改,完成RPC通訊

 最近一段時間有些事情耽擱了更新,抱歉各位了。   上一篇我們簡單的介紹了DotNetty通訊框架,並簡單的介紹了基於DotNetty實現了迴路(Echo)通訊過程。   我們來回憶一下上一個專案的整個流程: 當服務端啟動後,繫結並監聽(READ)設定的埠,比如1889。

一個線上音樂軟體的故事(五、我們開始寫程式碼吧)

讓我們開始寫程式碼吧 現在有了明確的功能需求,幾乎克服了所有的技術障礙,那麼就可以開始動手編寫這個音樂播放軟體了。 一、組織專案結構 這個故事所講的線上音樂播放軟體並沒有很複雜的功能需求,介面數量很少,沒有資料庫操作。這樣的專案幾乎可以任意組織程式碼檔案,甚至可以沒有任何結構,把所有的程

hexo next主題深度優化之加入pjax,實現區域性重新整理,我們的部落格上高速公路吧~~~~

特別宣告: 看不懂沒關係,往下讀,因為我寫的邏輯可能不是很清晰~ 本人原始碼在github上實在不懂的 git clone自己扣一扣,github在部落格中有連線 本人部落格mmmmmm.me 背景: 我有強迫症,遇到好的東西就想給自己整上去,在這裡想忠誠的奉勸大家一句,不要再搭

寫的不錯,轉--我們從NodeJS Streams開始從頭開始編寫一個Web伺服器

看完這篇文章後,真心的感覺閱讀原始碼的好處 Let's code a web server from scratch with NodeJS Streams https://www.codementor.io/ziad-saab/let-s-code-a-web-server-from-sc

我們構建一個Swift.Array

作者:Mike Ash,原文連結,原文日期:2015-04-17 譯者:灰s;校對:numbbbbb,Forelax;定稿:Forelax Swift 1.2 現已經作為 Xcode 6.3 的一部分而釋出,在新的 API 中有一個允許我們使用值型別建立高效的資料結構,比如 Swift 標準庫中的

我們開發一個瀏覽器(二)

讓我們開發一個瀏覽器(一)實現了在一個標籤中跳轉的建議瀏覽器,接下來新增一些功能,如位址列、多標籤、前進、後退、重新整理... 實現效果如下: 原始碼如下: from PyQt5.QtCore import QUrl, pyqtSignal, Qt from PyQt5.QtGui

我們開發一個瀏覽器(一)

設計介面 開啟Qt設計師,新建一個Main Window,移除選單欄和狀態列,如下圖: 拖入一個Table Widget,MainWindow佈局設定為網格佈局,為了美觀,邊距都設定為0,如下圖:   寫程式碼 使用pyuic5命令將ui檔案裝成py檔案,生成完成

我們開發一個非常好用的捲簾工具

版權宣告:未經作者允許不得轉載,此外掛不得用於商業用途。 目錄 開發環境 外掛開發 __init__.py map_swipe_plugin.py map_swipe_tool.py active deactivate canvasPressEvent can

【人生很短,做自己喜歡的事情罷。】人生是一場旅程,我們經歷了幾次輪迴,才換來這個旅程,而這個旅程很短,因此不妨大膽一些,不妨大膽一些去愛一個人,去攀一座山,去追一個夢······有很多事我都不明白,但我相信一件事,上天讓我們來到這個世界上,就是為了讓我們

人生是一場旅程,我們經歷了幾次輪迴,才換來這個旅程,而這個旅程很短,因此不妨大膽一些,不妨大膽一些去愛一個人,去攀一座山,去追一個夢······有很多事我都不明白,但我相信一件事,上天讓我們來到這個世...

人生很短,做自己喜歡的事情罷。(人生是一場旅程,我們經歷了幾次輪迴,才換來這個旅程,而這個旅程很短,因此不妨大膽一些,不妨大膽一些去愛一個人,去攀一座山,去追一個夢······有很多事我都不明白,但我相信一件事,上天讓我們來到這個世界上,就是為了讓我們創造

人生是一場旅程,我們經歷了幾次輪迴,才換來這個旅程,而這個旅程很短,因此不妨大膽一些,不妨大膽一些去愛一個人,去攀一座山,去追一個夢······有很多事我都不明白,但我相信一件事,上天讓我們來到這個世...

java中提供了對正則表示式的支援。 有的時候,恰當地使用正則,可以讓我們的工作事半功倍! 如下程式碼用來檢驗一個四則運算式中資料項的數目,請填寫劃線部分缺少的程式碼。 注意:只填寫缺少程式碼,不要

java中提供了對正則表示式的支援。 有的時候,恰當地使用正則,可以讓我們的工作事半功倍! 如下程式碼用來檢驗一個四則運算式中資料項的數目,請填寫劃線部分缺少的程式碼。 注意:只填寫缺少程式碼,不要寫任何多餘內容,例如,已有的雙引號。 public class A {pu

我們一步一步實現一個完整的 String 類:構造、拷貝、賦值、移動和析構

一、引言 我們在面試 C++ 相關崗位的時候,總會遇到這樣的筆試面試題: 請你實現一個 String 類 這道題,說簡單也簡單,說難也難,是一個考察 C++ 基礎的非常好的題目。正好在今天,我萌生了一個想法,那就是一步一步,一點一點,從構造析構,到

使用 NodeJs 實現一個本地介面資料系統,無需資料庫,前端獨立後臺開發

使用 NodeJs 實現本地介面系統,解決前後臺開發最後一公里 無資料庫的情況下,實現資料持久化,通過api url返回json 資料,提高前端開發效率! 專案地址 :local-ajax-api 下載完成安裝依賴就可使用 背景 前端開發工作中一個

來,我們一個網路爬蟲,下載頁面上所有的照片吧!

什麼是網路爬蟲? 網路爬蟲是一種非常有意思的程式。偌大的Internet,就像是一隻蜘蛛織成的大網:一個個超級連結就是蛛絲,將無數頁面連線起來,而網路爬蟲,則會沿著一根根蛛絲,爬遍每一個節點…… 網路爬蟲能幹嘛? 蜘蛛在網上爬來爬去,當然不是為了健身。它會在網上尋覓獵

樹講解(6)——我們異或吧

!= 情侶 rst back cst getch 能夠 代碼 st表 洛谷——P2420 讓我們異或吧 題目描述 異或是一種神奇的運算,大部分人把它總結成不進位加法. 在生活中…xor運算也很常見。比如,對於一個問題的回答,是為1

雖然關閉了VR電影工作室,但是Facebook依然致力虛擬現實

們的 height ora 創作 env 堅持 sof content 數字 原文標題:雖然關閉了VR電影工作室,但是Facebook依然致力於虛擬現實 Facebook的旗下Oculus在近日已經表示將關閉其虛擬制作部門Story Studio。 “我們現在正在進入V

從明天起,我們做一名調包俠

center alt 基礎設施 深入 程序包 images 反饋 learning 這樣的 原文首發於我的微信公眾號:GeekArtT。 從明天起,做一個幸福的人,餵馬、劈柴,周遊世界,然後做一名合格的調包俠。不再基礎設施上糾結不已,而是在前人的基礎上,進行地春暖花開

我們把KBEngine玩壞吧!如何定制我們自己的C++函數(一)

data ase erro glob alt ins sin 程序 all 為什麽不更新kbe warring的代碼解讀了,因為在我看來那個demo講完了實體就沒東西可講了,如果專心的看官方文檔和PPT的話demo的代碼後面沒任何難點了已經,單純的復制黏貼代碼實在太過無聊。

iOS11即將到來,我們具體了解下

src 代碼 理念 下載 們的 以及 區域 叠代 一條龍 谷歌開發者大會後,蘋果的WWDC終於也要來了,目前準確時間已經確定。 近日,蘋果官方發出的公告顯示,WWDC 2017將在北京時間6月6日淩晨1點正式進行,同時他們強調會進行現場直播,用戶可