2008-09-26 25 views
105

將Python(可能通過中間C代表)編譯成機器代碼有多大的可行性?將Python編譯爲機器碼是否可行?

大概就需要鏈接到一個Python運行時庫,併爲Python的本身將需要編譯(和鏈接中)太Python標準庫的任何部分。

此外,你需要,如果你想要做表達式的動態評價,但也許Python中的一個子集,並沒有讓這仍然是有用的捆綁Python解釋器。

它會提供任何速度和/或內存使用優勢?據推測,Python解釋器的啓動時間將被消除(儘管共享庫在啓動時仍然需要加載)。

+0

順便說一句,如果您要求「機器代碼」而不是目標代碼,您的問題將會變得更加清晰。 – 2008-09-26 10:20:32

+0

謝謝,我做了這個改變。 – 2009-08-13 14:45:39

回答

19

嘗試ShedSkin的Python到C++編譯器,但它遠非完美。如果只需要加速,還有Psyco - Python JIT。但恕我直言,這是不值得的努力。對於速度至關重要的代碼部分,最好的解決方案是將它們編寫爲C/C++擴展。

+4

僅供參考,ShedSkin放棄了對Windows的支持。 – sorin 2010-04-20 16:02:47

+2

@sorin:好吧,今天它支持Windows ... http://code.google.com/p/shedskin/downloads/detail?name=shedskin-0.9.1。exe&can = 2&q = – 2012-01-16 16:19:07

+0

速度最好的解決方案仍然可以是[PyPy](http://morepypy.blogspot.nl/2011/08/pypy-is-faster-than-c-again-string.html)。 – 2012-11-12 14:10:42

8

這似乎是合理的第一眼,但有Python中的很多普通的事情沒有直接可映射到一個C表示沒有帶過很多Python運行時支持。例如,鴨子打字想到。在Python中,讀取輸入的許多函數可以採用文件或類似文件的對象,只要它支持某些操作,例如。 read()或readline()。如果您考慮將這種類型的支持映射到C需要花費多少時間,您就開始想象Python運行時系統已經完成的各種事情。

有實用程序,如py2exe將捆綁Python程序和運行成單個可執行(儘可能)。

+0

如果我的目標是確保代碼編譯,如果靜態編譯語言(至少在我看來)在運行時不太可能爆炸,那該怎麼辦?是否有可能確定某些`foo.x`表達式不起作用,因爲`foo`在被調用時不會有`x`。是否有Python的靜態代碼檢查器? Python可以被編譯成一個.Net程序集...... – 2012-10-20 04:10:27

2

Psyco是一種即時(JIT)編譯器:用於Python的動態編譯器,運行速度快2-100倍,但需要大量內存。

總之:它運行得更快現有的Python軟件,在你的源沒有變化,但它不會編譯爲對象代碼的方式,C編譯器會。

2

Jython有一個針對JVM字節碼的編譯器。字節碼是完全動態的,就像Python語言本身一樣!很酷。 (是的,正如Greg Hewgill的回答所暗示的,字節碼確實使用Jython運行時,所以Jython jar文件必須與您的應用程序一起分發。)

13

PyPy是一個在Python中使用編譯爲本地代碼來重新實現Python的項目作爲實施策略之一(其他人是使用JIT的VM,使用JVM等)。他們編譯的C版本平均運行速度比CPython慢​​,但對於某些程序要快得多。

Shedskin是一個實驗性的Python-to-C++編譯器。

Pyrex是專門爲編寫Python擴展模塊而設計的語言。它旨在彌合Python的高級,易於使用的世界與C的凌亂低級世界之間的差距。

+3

Cython是Pyrex使用得更廣泛,更積極開發的友好分支。 – 2012-10-14 02:17:02

44

正如@Greg Hewgill所說,它有很好的理由並不總是可能的。但是,某些類型的代碼(如非常算法代碼)可以轉化爲「真實」機器代碼。

有幾種選擇:

  • 使用Psyco,動態地發出機器代碼。不過,您應該仔細選擇要轉換的方法/函數。
  • 使用Cython,這是一種Python- 語言編譯成一個Python C擴展
  • 使用PyPy,其中有來自RPy​​thon(一受限子集的Python不支持某些翻譯大多數Python的「動態」功能)到C或LLVM。
    • PyPy仍然是高度實驗性的
    • 並不是所有的擴展將出席

之後,你可以使用現有的包之一(凍結,Py2exe,PyInstaller)把一切都成一個二進制。

總而言之:你的問題沒有一般的答案。如果您的Python代碼對性能至關重要,請儘量使用盡可能多的內置功能(或者詢問「如何更快地創建我的Python代碼」問題)。如果這沒有幫助,請嘗試識別代碼並將其移植到C(或Cython)並使用擴展名。

2

答案是「是的,這是可能的」。您可以使用Python代碼並嘗試使用CPython API將其編譯爲等效的C代碼。事實上,過去有一個Python2C項目可以做到這一點,但我多年沒聽說過(回到Python 1.5天是我上次看到它的時候)。

您可以嘗試翻譯將Python代碼儘可能地轉換爲本地C,並在您需要實際的Python功能時回退到CPython API。上個月我一直在玩這個想法。然而,這是一項非常多的工作,並且大量的Python特性很難轉化爲C:嵌套函數,生成器,除了簡單類的簡單類之外的任何東西,涉及從模塊外部修改模塊全局變量的任何內容等等。等。

10

Nuitka是一個Python到C++編譯器,鏈接到libpython。這似乎是一個相對較新的項目。作者在pystone基準測試中聲明瞭CPython的speed improvement

2

這不會將Python編譯爲機器碼。但是允許創建一個共享庫來調用Python代碼。

如果你正在尋找的是一個簡單的方法來從C運行Python代碼而不依賴於execp的東西。您可以從包含幾個調用Python embedding API的Python代碼生成共享庫。那麼這個應用程序就是一個共享庫,你可以在許多其他的庫/應用程序中使用它。

下面是一個簡單的例子,它創建一個共享庫,您可以鏈接到一個C程序。共享庫執行Python代碼。

將要執行的Python文件是pythoncalledfromc.py

# -*- encoding:utf-8 -*- 
# this file must be named "pythoncalledfrom.py" 

def main(string): # args must a string 
    print "python is called from c" 
    print "string sent by «c» code is:" 
    print string 
    print "end of «c» code input" 
    return 0xc0c4 # return something 

您可以python2 -c "import pythoncalledfromc; pythoncalledfromc.main('HELLO')嘗試。它會輸出:

python is called from c 
string sent by «c» code is: 
HELLO 
end of «c» code input 

共享庫將通過以下來定義由callpython.h

#ifndef CALL_PYTHON 
#define CALL_PYTHON 

void callpython_init(void); 
int callpython(char ** arguments); 
void callpython_finalize(void); 

#endif 

關聯callpython.c是:

// gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so 

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <python2.7/Python.h> 

#include "callpython.h" 

#define PYTHON_EXEC_STRING_LENGTH 52 
#define PYTHON_EXEC_STRING "import pythoncalledfromc; pythoncalledfromc.main(\"%s\")" 


void callpython_init(void) { 
    Py_Initialize(); 
} 

int callpython(char ** arguments) { 
    int arguments_string_size = (int) strlen(*arguments); 
    char * python_script_to_execute = malloc(arguments_string_size + PYTHON_EXEC_STRING_LENGTH); 
    PyObject *__main__, *locals; 
    PyObject * result = NULL; 

    if (python_script_to_execute == NULL) 
    return -1; 

    __main__ = PyImport_AddModule("__main__"); 
    if (__main__ == NULL) 
    return -1; 

    locals = PyModule_GetDict(__main__); 

    sprintf(python_script_to_execute, PYTHON_EXEC_STRING, *arguments); 
    result = PyRun_String(python_script_to_execute, Py_file_input, locals, locals); 
    if(result == NULL) 
    return -1; 
    return 0; 
} 

void callpython_finalize(void) { 
    Py_Finalize(); 
} 

可以用下面的命令編譯它:

gcc `python2.7-config --ldflags` `python2.7-config --cflags` callpython.c -lpython2.7 -shared -fPIC -o callpython.so 

創建一個名爲callpythonfromc.c文件,該文件包含以下內容:

#include "callpython.h" 

int main(void) { 
    char * example = "HELLO"; 
    callpython_init(); 
    callpython(&example); 
    callpython_finalize(); 
    return 0; 
} 

編譯並運行:

gcc callpythonfromc.c callpython.so -o callpythonfromc 
PYTHONPATH=`pwd` LD_LIBRARY_PATH=`pwd` ./callpythonfromc 

這是一個非常簡單的例子。它可以工作,但取決於庫,它可能仍然很難將C數據結構序列化爲Python,並且從Python到C也很難。事情可能會有些自動化...事實可能會有所自動化...

Nuitka可能會有所幫助。

也有numba但他們都沒有打算做你想要的東西。只有在指定如何將Python類型轉換爲C類型或可以推斷該信息時,纔可以從Python代碼生成C頭文件。有關Python ast分析器,請參閱python astroid

相關問題