都有Python 了,還要什麼編譯器!

2021-01-07 CSDN

編譯的目的是將源碼轉化為機器可識別的可執行程序,在早期,每次編譯都需要重新構建所有東西,後來人們意識到可以讓編譯器自動完成一些工作,從而提升編譯效率。但「編譯器不過是用於代碼生成的軟機器,你可以使用你想要的任何語言來生成代碼」,真的是必要的嗎?

作者 | Oleksandr Kaleniuk

譯者 | 虎說

責編 | 仲培藝

誠然,編譯器可以為你生成高性能的代碼,但是你真的需要編譯器嗎?另一種方法是用 Assembly 編寫程序,雖然有點誇大,但這種方法有兩個主要缺陷:

1. 彙編代碼不可移植;

2. 雖然在現代工具的輔助下變得容易了些,但 Assembly 編程仍然需要大量繁瑣的工作。

值得慶幸的是,我們都生活在二十一世紀,這兩個問題都已得到解決。第一個解決方案是 LLVM,最初,它意味著「低級虛擬機」,這正是我們可以確保可移植性的原因。簡而言之,它需要用一些非常低級別的與硬體無關語言編寫的代碼,並為特定的硬體平臺返回一些高度優化的原生代碼。使用 LLVM,我們既具有低級編程的強大功能,又具有面向硬體微優化的自動化功能。

第二個問題的解決方法是使用「腳本」語言,Scheme、Python、Perl,甚至 bash 或 AWK 都可以消除繁瑣的工作。

實驗計劃

首先,讓我們生成一個完全內聯展開的解決方案,並將其嵌入到基準測試代碼中。該計劃如下:

1. 使用 Clang 為基準生成 LLVM 中間代碼,該基準用於測量 solve_5,一個不存在的函數;

2. 使 Python 在 LLVM 中生成線性求解器(linear solver)代碼;

3. 使用 Python 腳本測試基準,用生成求解器替換 solve_5 調用;

4. 使用 LLVM 靜態編譯器將中間代碼轉換為機器代碼;

5. 使用 GNU 彙編器和 Clang 的連結器將機器代碼轉換為可執行的二進位文件。

這就是它在 Makefile 中的樣子:

Python 部分

我們需要 Python 中的線性求解器(linear solver),就像我們使用 C 和 C ++ 一樣,此處代碼為:

# this generates n-solver in LLVM code with LLVMCode objects.# No LLVM stuff yet, just completely Pythonic solutiondefsolve_linear_system(a_array, b_array, x_array, n_value):defa(i, j, n):if n == n_value:return a_array[i * n_value + j]return a(i, j, n+1)*a(n, n, n+1) - a(i, n, n+1)*a(n, j, n+1)defb(i, n):if n == n_value:return b_array[i]return a(n, n, n+1)*b(i, n+1) - a(i, n, n+1)*b(n, n+1)defx(i):d = b(i,i+1)for j in range(i): d -= a(i, j, i+1) * x_array[j]return d / a(i, i, i+1)for k in range(n_value): x_array[k] = x(k)return x_array

當我們用數字運行時,我們可以得到數字。但我們想要代碼,因此,我們需要製作一個假裝成數字的對象(Object)來探測算法。該對象記錄下算法想要執行的每一個操作,並準備好集成 LLVM 中間語言。

# this is basically the whole LLVM layerI = 0STACK = []classLLVMCode:# the only constructor for now is by double* instructiondef__init__(self, io, code = ''):self.io = ioself.code = codedef__getitem__(self, i):global I, STACK copy_code = "%" + str(I+1) copy_code += " = getelementptr inbounds double, double* " copy_code += self.io +", i64 " + str(i) + "\n" copy_code += "%" + str(I+2) copy_code += " = load double, double* %" + str(I+1) copy_code += ", align 8\n" I += 2 STACK += [I]return LLVMCode(self.io, copy_code)def__setitem__(self, i, other_llvcode): global I, STACKself.code += other_llvcode.codeself.code += "%" + str(I+1)self.code += " = getelementptr inbounds double, double* "self.code += self.io +", i64 " + str(i) + "\n"self.code += "store double %" + str(I)self.code += ", double* %" + str(I+1) + ", align 8\n" I += 1 STACK = STACK[:-1]returnselfdefgeneral_arithmetics(self, operator, other_llvcode): global I, STACKself.code += other_llvcode.code;self.code += "%" + str(I+1) + " = f" + operatorself.code += " double %" + str(STACK[-2]) + ", %"self.code += str(STACK[-1]) + "\n"; I += 1 STACK = STACK[:-2] + [I]returnselfdef__add__(self, other_llvcode):returnself.general_arithmetics('add', other_llvcode)def__sub__(self, other_llvcode):returnself.general_arithmetics('sub', other_llvcode)def__mul__(self, other_llvcode):returnself.general_arithmetics('mul', other_llvcode)def__div__(self, other_llvcode):returnself.general_arithmetics('div', other_llvcode)

接著,當我們使用這種對象運行求解器時,我們得到了一個用 LLVM 中間語言編寫的全功能線性求解器。然後我們將其放入基準代碼中進行速度測試(看它有多快)。

LLVM 中的指令有編號,我們希望保存枚舉,因此將代碼插入到基準測試中的函數很重要,但也不是很複雜。

# this replaces the function call# and updates all the instructions' indicesdefreplace_call(text, line, params):global I, STACK# '%12 ' -> 12I = int(''.join( [xi for xi in params[2] if xi.isdigit()] )) first_instruction_to_replace = I + 1 STACK = [] replacement = solve_linear_system( LLVMCode(params[0]), LLVMCode(params[1]), LLVMCode(params[2]), 5).code delta_instruction = I - first_instruction_to_replace + 1for i in xrange(first_instruction_to_replace, sys.maxint): not_found = sum( [text.find('%' + str(i) + c) == -1for c in POSSIBLE_CHARS_NUMBER_FOLLOWS_WITH] )if not_found == 4:# the last instruction has already been substitutedbreak new_i = i + delta_instructionfor c in POSSIBLE_CHARS_NUMBER_FOLLOWS_WITH:# substitute instruction number text = text.replace('%' + str(i) + c, '%' + str(new_i) + c)return text.replace(line, replacement)

實現解算器的整段代碼提供了 Python-to-LLVM 層,其中代碼插入只有 100 行!

另附 GitHub 連結:https://github.com/akalenuk/wordsandbuttons/blob/master/exp/python_to_llvm/exp_embed_on_call/substitute_solver_call.py

基準

基準測試本身在 C 中。當我們運行 Makefile 時,它對 solve_5 的調用被 Python 生成的 LLVM 代碼所取代。

Step 1. Benchmark C source code

Step 2. LLVM 彙編語言

Step 3. 調用替換後的 LLVM

Step 4. 本地優化裝配

最值得注意的是 Python 腳本生成的超冗長中間代碼如何變成一些非常緊湊且非常有效的硬體代碼。同時它也是高度標量化的,但它是否足以與 C 和 C++ 的解決方案競爭呢?

以下是三種情況的近似數字(帶有技巧的 C、C++ 與基於 LLVM 的 Python 的性能對比):

1. C 的技巧對 Clang 來說並不適用,因此測量 GCC 版本,其平均運行大約 70 毫秒;

2. C++ 版本是用 Clang 構建的,運行時間為 60 毫秒;

3. Python 版本(此處描述的版本)僅運行 55 毫秒。

當然,這種加速並不是關鍵,但這表明你可以用 Python 編寫出勝過用 C 或 C++ 編寫的程序。這也就暗示你不必學習一些特殊語言來創建高性能的應用程式或庫。

結論

快速編譯語言和慢速腳本語言之間的對立不過是虛張聲勢。原生代碼生成的可能不是核心功能,而是類似於可插拔選項。像是 Python 編譯器 Numba 或 Lua 的 Terra,其優勢就在於你可以用一種語言進行研究和快速原型設計,然後使用相同的語言生成高性能的代碼。

高性能計算沒有理由保留編譯語言的特權,編譯器只是用於代碼生成的軟機器。你可以使用你想要的任何語言生成代碼,我相信如果你願意,你可以教 Matlab 生成超快的 LLVM 代碼。

本文涉及的所有測試均在 Intel(R)Core(TM)i7-7700HQ CPU @ 2.80GHz 上進行,代碼使用 Clang 3.8.0-2ubuntu4 和 g++5.4.0 編譯。基準測試原始碼:https://github.com/akalenuk/wordsandbuttons/tree/master/exp/python_to_llvm

原文:https://wordsandbuttons.online/outperforming_everything_with_anything.html本文為 CSDN 翻譯,如需轉載,請註明來源出處。

相關焦點

  • 11 個優秀的 Python 編譯器和解釋器
    它有一些 CPython 的特性。它預裝了一些針對數據科學和機器學習的流行庫,例如 Numpy、Pandas 和 Scipy。它帶有 C/C++ 編譯器,大多數時候不會用到。除此之外,它只有 Python 編譯器,沒有其它包。網址:https://winpython.github.io4.Skulpt
  • python 開發編譯器
    >如需轉載,發送「轉載」二字查看說明引言最近剛剛用python寫完了一個解析protobuf文件的簡單編譯器,深感ply實現詞法分析和語法分析的簡潔方便。ply使用簡介如果你不是從事編譯器或者解析器的開發工作,你可能從未聽說過ply。ply是基於python的lex和yacc,而它的作者就是大名鼎鼎Python Cookbook, 3rd Edition的作者。可能有些朋友就納悶了,我一個業務開發怎麼需要自己寫編譯器呢,各位編程大牛說過,中央決定了,要多嘗試新的東西。
  • Python入門個人經驗之文本編譯器(IDLE、Anaconda、Pycharm)
    這篇文章主要是根據自己的血淚史想介紹一下Python的文本編譯器。在Python學習之前,選擇一個好的文本編譯器能事半功倍。入門使用的教材是《Python編程從入門到實踐》,開始使用的版本是直接在官網下載Python3.7.0版本,使用的文本編譯器是自帶的IDLE。
  • Python編譯器與解釋器
    但是,在此之前,還要先說說編譯器與解釋器相關的內容。如果這部分內容,讓你覺得難以理解或不能完全明白,可以暫時跳過,等以後再回過頭來重新讀一遍。一、數據的表示方式我們都知道,現實生活中,數字的表示方式有很多種,常見的有二進位、八進位、十進位和十六進位。
  • 將Notepad++配置為Python編譯器(方法二)
    將Notepad++配置成為腳本編譯器安裝Python, 安裝最新版本的Notepad++安裝NppExec插件;a.打開Notepad++(編輯任何一個txt文檔即可),b.<PYTHON_HOME> : 表示安裝在你本機的python的路徑,一定要帶python.exe,例如我的路徑:C:\ Python27\python.exe2. 如果給python添加了環境變量的,可以直接使用python即可.使用<PYTHON_HOM>的方便在於,如果你安裝了好幾個版本的python,那麼可以通過不同的路徑來編譯不同版本的腳本, b.
  • Numba:用CUDA加速的高性能Python編譯器
    使python如此受歡迎的因素有很多,包括其乾淨的、表達性的語法和標準的數據結構,綜合的「內置電池」標準庫,優秀的文檔,庫和工具的廣泛生態系統,專業支持的可用性,以及大而開放的社區。不過,也許最重要的原因是,像Python這樣的動態類型化的解釋語言能夠提高生產率。Python足夠敏捷與靈活,使它成為快速原型開發的一種偉大語言,同時也是構建完整系統的語言。
  • python解釋器到底是什麼?
    有很多入門學習python的同學都沒有搞清python解釋器是怎麼回事,所以今天在這裡追根溯源的解釋一下。 計算機程式語言 從計算機程式語言說起,它主要分為三類:機器語言、彙編語言、高級語言。
  • 科悟學院講解Python是什麼以及Python的應用
    人工智慧相信現在大多數人都已經不陌生了,逐漸進入千家萬戶,但是知道人工智慧是什麼編寫的嗎?其實人工智慧現在最主要的是利用Python語言去編寫的,有人會問Python是什麼?今天科悟學院的講師就為你介紹一下Python是什麼以及Python的應用,希望對你有所幫助。Python 是一種解釋型、面向對象、動態數據類型的強類型高級程序設計語言。
  • 華為方舟編譯器深入解讀:已有45款第三方應用
    程序原始碼中的任何信息對於程序分析和優化都是有幫助的,所以方舟IR的目標是儘可能完整詳細地提供源程序的信息。-paramiko python-paramiko python-jenkins python-requests python-xlwt libglib2.0-dev libpixman-1-dev linux-libc-dev:i386sudo apt-get -y install gcc-5-aarch64-linux-gnu g++-5-aarch64-
  • Python是什麼?學習Python用什麼編譯器?
    因此,Perl語言中總是有多種方法來做同一件事的理念在Python開發者中通常是難以忍受的。Python開發者的哲學是用一種方法,最好是只有一種方法來做一件事。在設計Python語言時,如果面臨多種選擇,Python開發者一般會拒絕花俏的語法,而選擇明確的沒有或者很少有歧義的語法。
  • 什麼是C語言的編譯器?從計算機原理的角度談編譯器
    相信你們已經知道什麼是機器語言和彙編語言,如果有不知道的朋友可以關注參考編者的另外一篇文章:C語言基礎:二進位和計算機語言雜談(編程新手福利),當然也可以自行百度。早期的機器語言沒有編譯器的概念,因為機器語言不過是很多的0和1,CPU(處理器)能夠直接識別機器語言,C語言本身是為了提高開發效率而開發出的新語言,語義上幾乎和現實世界表達意思一致,但是這樣高級的語義可就難倒了計算機,它不認識像if-else、while等單詞,那麼計算機怎麼識別C程序的呢,這就引出編譯器的概念了。
  • Python,你到底是什麼程式語言?
    之前說了很多Python的一些基本認識,那今天,我們來聊聊,她到底是什麼程式語言:python是什麼程式語言程式語言主要從以下幾個角度為進行分類,編譯型和解釋型、靜態語言和動態語言、強類型定義語言和弱類型定義語言,每個分類代表什麼意思呢
  • Python程序執行過程與字節碼
    我們每天都要編寫一些 Python 程序,或者用來處理一些文本,或者是做一些系統管理工作。此外,程序執行過程中可能會有 .pyc 文件生成,這些文件又有什麼作用呢?帶著這些問題我們開始本節的探索。Python程序執行過程你也許聽過這樣的說法: Python 是一種解釋性語言。這意味著 Python 程序不用編譯,只需要用一個解釋器來執行。事實真的是這樣嗎?
  • 讓Python代碼更快運行的 5 種方法
    選擇了腳本語言就要忍受其速度,這句話在某種程度上說明了Python作為腳本語言的不足之處,那就是執行效率和性能不夠亮。儘管Python從未如C和Java一般快速,但是不少Python項目都處於開發語言領先位置。Python很簡單易用,但大多數人使用Python都知道在處理密集型cpu工作時,它的數量級依然低於C、Java和JavaScript。
  • 小白使用VS code編寫python,如何優雅避坑
    01 寫這篇文章的初衷接下來,「小白學記」會陸續推出python的學習歷程。當然,寫這篇文章的時候,小編在python方面仍然是一個不折不扣的小白,知識掌握的還相當有限。當寫到這篇文章的時候,小編也愈發感覺到做「小白學記」的意義。拿這次要講的VS code編譯器來說,我們在網上隨便一查就可以找到很多資源,它們會給出很多詳細的教程。
  • Intel的「霸道」:深究編譯器對CPU性能的影響-Intel,AMD,編譯器...
    全文內容非常多,基礎原理、SSE/AVX指令集特點、編譯器優化歷史等等都有涉及,這部分內容就不再翻譯了,因為內容過於專業,大部分人都不是專業人士,讀起來晦澀難懂,同行超能網為我們選取了其中的重點內容。微軟的編譯器在Core i7上甚至要比AVX版還要快12%,後者本來應該是有最佳性能的。結果有些出乎意料,SSE 4.1/4.2/AVX指令在i7/FX處理器上都是最慢的,但是Phenom X4上並非如此。
  • 世界上第一個C語言編譯器是怎麼編寫的?它為什麼能夠用C語言編寫?
    不知道大家有沒有想過一個問題:C語言編譯器為什麼能夠用C語言編寫? 今天小編就帶大家一探究竟!這些操作,C語言都是可以實現的。 所以用C語言來做C語言的編譯器是完全可行的。 但是,歷史上的第一個C語言編譯器,肯定不是C語言寫的,因為在沒有編譯器時,無法把C語言轉換成可執行文件。只要有了第一版其它語言的編譯器,就可以用C語言寫編譯器了。
  • 方舟編譯器帶來更多話語權,國產編譯器仍需提高自主性
    2019 年 8 月底,華為方舟編譯器(OpenArkCompiler)正式開源。一年多來,方舟編程體系陸續實現了編譯器、引擎、調試器的開源。方舟編譯器的「走紅」,也讓編譯器這一技術性話題逐漸廣為人知。那麼,編譯器的重要性有哪些?中國在編譯器方面是怎樣的現狀?還有哪些不足?
  • 用Python使用C語言程序(Windows平臺)
    (http://www.jianshu.com/p/09994c9d8489)上面兩篇博客已經寫得很詳細,但是都是基於linux平臺和mac,我這裡算是作為一篇windows平臺的補充和總結,還有自己踩的一些坑,跟大家分享。要使用python使用c語言編寫的程序,大致分成兩種方法,一種是純手寫,一種是用第三方的接口工具。本文將分成兩部分分別講述。
  • Java、JavaScript、C、C++、PHP、Python都是用來開發什麼?
    用任何程式語言來開發程序,都是為了讓計算機幹活,比如編寫一篇文章,下載一首MP3等,而計算機幹活的CPU只認識機器的指令,所以,儘管不同的程式語言差異極大,最後都得「翻譯」成CPU可以執行的機器指令。理論上任何語言幹任何事情幾乎都可以, 但是主要幹什麼那就不一樣了。