11月 04, 2008

【語言】直譯與編譯 - Interpretation and Compilation

@
  之前,我們提到過高階語言(high-level language)須經由轉換的動作,將原始的程式碼「翻譯」成機器看得懂的二進位機器碼。一般而言,我們可以因這種轉換的動作的不同,將程式語言分為編譯式語言(compiled language)直譯式語言(interpreted language)兩種。


  編譯式語言(如 C、C++、Pascal、Delphi 等)利用編譯器(compiler)針對原始程式先進行分析(analysis)以及前置處理(preprocess)的動作,並檢查程式中是否存在文法錯誤之後,再將之全部轉換為某種中介的目標語言(target language),稱之為目的檔(object file)

  將原始碼轉換為目的檔之後,我們還需要經由連結器(linker)連結一個或多個目的檔與外部函式庫(library),轉換成機器碼以形成可執行檔(executable file)。此後除非程式有所更改,否則不需要再次進行編譯的動作,便可以直接利用可執行檔執行使用了。


  而直譯式語言(如 VB、Python、REBOL、Ruby 等)相對於編譯式語言,其執行前並不會產生任何目的檔或是可執行檔,而是在執行當中才利用直譯器(interpreter)將執行到的區塊進行解析(parse),再執行對應的機器碼。因此,其執行效率相較於編譯式語言是比較低的。


  讓我們將高階語言比喻為英文、機器語言比喻為中文,以「演講」為範例描述兩種方式的區別。

  若是一名演講者有一份英文的演說稿,但是必須以我們聽得懂的中文(假設我們只聽得懂中文)進行演說,則以「編譯」的方式,這名演講者必須先將整篇文章讀過,且全部翻譯成中文的演說稿(意義近似於前面提到的「可執行檔」)。此後,除非演講的內容改變,無論演講者作幾次演說,他都不需要將演講稿重新翻譯。因為他已經有中文的演說稿了!

  而若以「直譯」的方式,這名演講者在演講前,並不會對這篇演講稿進行任何翻譯的動作。而是在實際演講時,才逐步翻譯目前演講到的內容。所以,這名演講者每次進行演說時,都需要將演講稿重新翻譯一次。


  由以上這些,我們可以知道:由於編譯式語言需要一次將原始碼全部「翻譯」,因此會先花上較長的時間進行編譯的動作。而直譯式語言則因為執行時才將原始碼進行轉換,因此實際上的執行效率相較於編譯式語言是比較低的。

  (對直譯的理解有誤,舉例不適當。感謝網友 mitnick 指正。)


  當然,除了編譯式語言與直譯式語言之外,近來也出現了介於兩者之間的「混合式語言(hybrid language)」,例如著名的 JavaC#

  其原理為先將原始程式碼「編譯」成其虛擬機器(virtual machine,VM)的「機器語言」(這裡指的是虛擬機器的機器語言,不是實際上的機器語言)。等到要執行程式時,其再藉由虛擬機器「直譯」這些虛擬機器碼。理所當然的,這種程式語言的執行效率也就落在直譯式語言與編譯式語言之間。

  然而,在某些主流平台(例如 x86)上,程式在第一次執行時可以透過即時編譯(just-in-time compilation,JIT)的方式,編譯成其所在平台的機器碼。經過這個步驟,其執行速度是可以相當於、甚至是優於編譯式語言的。(感謝網友 kgame 補充,mitnick 指正。)


  而不論是編譯式語言、直譯式語言、還是混合式語言,都各擁有其優點與缺點。至於孰優孰劣,就只能視情況、見仁見智囉。

4 回覆:

kgame 智涵 提到...

Java和C#要依他的Runtime來決定該平台是否為直譯或編譯
在一般常見的x86環境中執行Java和C#的EXE檔
每個函數第一次執行前會從VM機器碼再透過JIT(Just in Time)
即時編譯器編譯成x86機器碼
經過這個步驟後 ,執行速度可以與C/C++不相上下
不過較罕見的平台大部分只有開發直譯器
所以速度上就差多了

Unknown 提到...

感謝補充。我已於文章加上你所說的內容。

mitnick 提到...

你對直譯的觀念有誤
以編譯語言來說, 編譯可以粗略分為 parse 跟 code generation 兩個部份. 執行時就是直接執行 generation 出來的機器碼.
但在直譯語言中, 每一次執行都需要 parse 及執行對應的機器碼. 所以他所多做的部份是在於 parse.根本不會有所謂的翻譯.
舉例來說. 比如說你寫 c = a + b. 在編譯語言中可能會直接編譯成 add c, a, b. 但是在直譯語言中. 則會先進行 parse, 發現要執行的內容是加法,從而執行 add c, a, b. 所以直譯式語言在執行的 overhead 就是 parse 跟 dispatch 對應程式碼的部份.
所謂的 JIT 就是幫助你拿掉那部份的 overhead. 所以你最多只能跟編譯式語言一樣快. 要超過是不可能的(何況你還需要花費編譯所需的時間)

Unknown 提到...

看來當初看書的時候就理解錯誤了,感謝你的指正及詳細說明:)

張貼留言