Czy można skompilować Pythona do kodu maszynowego?

130

Jak wykonalne byłoby skompilowanie Pythona (prawdopodobnie poprzez pośrednią reprezentację C) do kodu maszynowego?

Prawdopodobnie musiałoby to być linkowane do biblioteki wykonawczej Pythona, a wszelkie części standardowej biblioteki Pythona, które same byłyby Pythonem, również musiałyby zostać skompilowane (i połączone).

Ponadto, jeśli chciałbyś przeprowadzić dynamiczną ocenę wyrażeń, musiałbyś spakować interpreter Pythona, ale być może podzbiór Pythona, który na to nie pozwalał, byłby nadal przydatny.

Czy zapewniłoby to jakieś korzyści związane z szybkością i / lub wykorzystaniem pamięci? Przypuszczalnie czas uruchamiania interpretera Pythona zostałby wyeliminowany (chociaż biblioteki współdzielone nadal wymagałyby ładowania podczas uruchamiania).

Andy Balaam
źródło
2
Przy okazji, twoje pytanie byłoby IMHO jaśniejsze, gdybyś poprosił o „kod maszynowy” zamiast kodu wynikowego.
Torsten Marek

Odpowiedzi:

31

Wypróbuj kompilator ShedSkin Python-to-C ++, ale jest daleki od doskonałości. Istnieje również Psyco - Python JIT, jeśli potrzebne jest tylko przyspieszenie. Ale IMHO to nie jest warte wysiłku. W przypadku części kodu o krytycznym znaczeniu dla szybkości najlepszym rozwiązaniem byłoby zapisanie ich jako rozszerzeń C / C ++.

cleg
źródło
5
FYI, ShedSkin porzucił obsługę systemu Windows.
sorin
2
@sorin: cóż, dziś obsługuje
2
Najlepszym rozwiązaniem, szybkim, nadal może być PyPy .
Cees Timmerman,
shedskin nie wykonał żadnej pracy od około dwóch lat. :(
Perkins
53

Jak mówi @Greg Hewgill, istnieją dobre powody, dla których nie zawsze jest to możliwe. Jednak niektóre rodzaje kodu (jak kod bardzo algorytmiczny) można przekształcić w „prawdziwy” kod maszynowy.

Istnieje kilka opcji:

  • Użyj Psyco , który dynamicznie emituje kod maszynowy. Powinieneś jednak uważnie wybrać metody / funkcje do konwersji.
  • Użyj Cythona , który jest językiem podobnym do Pythona, który jest wkompilowany w rozszerzenie Python C.
  • Użyj PyPy , który ma tłumacza z RPythona ( ograniczony podzbiór Pythona, który nie obsługuje niektórych z najbardziej „dynamicznych” funkcji Pythona) na C lub LLVM.
    • PyPy jest nadal wysoce eksperymentalny
    • nie wszystkie rozszerzenia będą obecne

Następnie możesz użyć jednego z istniejących pakietów (freeze, Py2exe, PyInstaller), aby umieścić wszystko w jednym pliku binarnym.

Podsumowując: nie ma ogólnej odpowiedzi na twoje pytanie. Jeśli masz kod w Pythonie, który jest krytyczny dla wydajności, spróbuj użyć jak największej liczby wbudowanych funkcji (lub zadaj pytanie „Jak przyspieszyć mój kod w Pythonie”). Jeśli to nie pomoże, spróbuj zidentyfikować kod i przenieś go do C (lub Cython) i użyj rozszerzenia.

Torsten Marek
źródło
3
Pypy jest następcą Psyco
bcattle
19

py2c ( https://github.com/pradyun/Py2C ) potrafi konwertować kod Pythona do c / c ++ Jestem samodzielnym programistą py2c.

Ramchandra Apte
źródło
To wygląda na przydatne narzędzie. Czy nadal jest utrzymywany?
Anderson Green,
@AndersonGreen Jest na wczesnym etapie rozwoju, kiedy ostatnio nad nim pracowałem (prawdopodobnie podobnie teraz). Opuściłem projekt, ponieważ ̶'̶m̶ ̶b̶u̶s̶y̶ Jestem leniwy. Jeśli nie zauważyłeś tekstu „Ważne”, został on teraz przeniesiony do GitHub.
Ramchandra Apte,
Odsyłacz wskazuje na niepokonanego instalatora , który wydaje się być innym projektem. Czy py2c jest nadal dostępny na GitHubie?
Anderson Green,
@AndersonGreen Wow, który był niezauważony przez tak długi czas! Proszę bardzo.
Ramchandra Apte
Link na code.google.com/p/py2c nadal wskazuje na unvanquished-installer, więc należy go teraz zaktualizować.
Anderson Green,
15

Nuitka to kompilator Pythona do C ++, który łączy się z libpythonem. Wydaje się, że jest to stosunkowo nowy projekt. Autor twierdzi, że w teście porównawczym pystone poprawia prędkość w stosunku do CPython.

bydło
źródło
15

PyPy to projekt reimplementacji Pythona w Pythonie, przy użyciu kompilacji do kodu natywnego jako jednej ze strategii implementacji (inne to maszyny wirtualne z JIT, używanie JVM itp.). Ich skompilowane wersje C działają średnio wolniej niż CPython, ale w przypadku niektórych programów znacznie szybciej.

Shedskin to eksperymentalny kompilator Pythona do C ++.

Pyrex to język zaprojektowany specjalnie do pisania modułów rozszerzeń Pythona. Został zaprojektowany, aby wypełnić lukę między przyjemnym, wysokopoziomowym, łatwym w użyciu światem Pythona a niechlujnym, niskopoziomowym światem C.

pdc
źródło
3
Cython jest szerzej używanym i aktywniej rozwijanym przyjaznym widelcem Pyrexu.
Mike Graham
„ładny, wysokopoziomowy, łatwy w użyciu świat Pythona i niechlujny, niskopoziomowy świat C” - zabawne Pomyślałem sobie, że C i assembler są „ładne” i proste, a Python żyje w ” niechlujny ”,„ świat wysokiego poziomu ”
Reversed Engineer
10

Pyrex jest podzbiorem języka Python, który kompiluje się do C, wykonanym przez faceta, który jako pierwszy zbudował listy składane dla Pythona. Został opracowany głównie do budowania opakowań, ale może być używany w bardziej ogólnym kontekście. Cython jest bardziej aktywnie utrzymywanym widelcem pyrexu.

ConcernedOfTunbridgeWells
źródło
2
Cython jest szerzej używanym i aktywniej rozwijanym przyjaznym widelcem Pyrexu.
Mike Graham
3

Jython ma kompilator przeznaczony dla kodu bajtowego JVM. Kod bajtowy jest w pełni dynamiczny, podobnie jak sam język Python! Bardzo fajny. (Tak, jak sugeruje odpowiedź Grega Hewgilla, kod bajtowy korzysta ze środowiska wykonawczego Jython, więc plik jar Jython musi być rozpowszechniany wraz z aplikacją).

Chris Jester-Young
źródło
2

Psyco jest rodzajem kompilatora just-in-time (JIT): kompilator dynamiczny dla Pythona, uruchamia kod 2-100 razy szybciej, ale wymaga dużo pamięci.

Krótko mówiąc: uruchamia istniejące oprogramowanie Python znacznie szybciej, bez zmian w kodzie źródłowym, ale nie kompiluje się do kodu obiektowego w taki sam sposób, jak kompilator C.

Pierre-Jean Coudert
źródło
2

Odpowiedź brzmi: „Tak, jest to możliwe”. Możesz wziąć kod Pythona i spróbować skompilować go do odpowiednika kodu C za pomocą CPython API. W rzeczywistości istniał kiedyś projekt Python2C, który właśnie to robił, ale nie słyszałem o tym od wielu lat (w Pythonie 1.5 dni to ostatni raz.)

Możesz próbować przetłumaczyć kod Pythona na natywne C tak bardzo, jak to możliwe, i wrócić do interfejsu API CPython, gdy potrzebujesz rzeczywistych funkcji Pythona. Sam bawiłem się tym pomysłem przez ostatni miesiąc lub dwa. Jest to jednak bardzo dużo pracy, a ogromna ilość funkcji Pythona jest bardzo trudna do przetłumaczenia na C: funkcje zagnieżdżone, generatory, wszystko oprócz prostych klas z prostymi metodami, wszystko, co wiąże się z modyfikowaniem globalnych modułów spoza modułu itp. itp.

Thomas Wouters
źródło
2

To nie kompiluje Pythona do kodu maszynowego. Ale pozwala stworzyć współdzieloną bibliotekę do wywoływania kodu Pythona.

Jeśli szukasz łatwego sposobu na uruchomienie kodu Pythona z poziomu C bez polegania na plikach execp. Możesz wygenerować bibliotekę współdzieloną z kodu Pythona opakowanego w kilka wywołań API osadzania w Pythonie . Cóż, aplikacja jest biblioteką współdzieloną, tak że można jej używać w wielu innych bibliotekach / aplikacjach.

Oto prosty przykład, który tworzy bibliotekę współdzieloną, którą można połączyć z programem C. Biblioteka współdzielona wykonuje kod Pythona.

Plik Pythona, który zostanie wykonany, to 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

Możesz to wypróbować python2 -c "import pythoncalledfromc; pythoncalledfromc.main('HELLO'). Wyświetli:

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

Biblioteka współdzielona zostanie zdefiniowana przez callpython.h:

#ifndef CALL_PYTHON
#define CALL_PYTHON

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

#endif

Powiązane callpython.cto:

// 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();
}

Możesz go skompilować za pomocą następującego polecenia:

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

Utwórz plik o nazwie, callpythonfromc.cktóry zawiera następujące elementy:

#include "callpython.h"

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

Skompiluj i uruchom:

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

To jest bardzo podstawowy przykład. Może działać, ale w zależności od biblioteki może być nadal trudno serializować struktury danych C do Pythona iz Pythona do C. Wszystko można nieco zautomatyzować ...

Nuitka może być pomocna.

Jest też numba, ale obaj nie mają na celu robienia dokładnie tego, co chcesz. Generowanie nagłówka C z kodu Pythona jest możliwe, ale tylko wtedy, gdy określisz sposób konwersji typów Python na typy C lub możesz wywnioskować te informacje. Zobacz Python astroid dla analizatora Python ast.

amirouche
źródło