wake-up-neo.com

Ist es möglich, Python in Maschinencode zu kompilieren?

Wie machbar wäre es, Python (möglicherweise über eine C-Zwischendarstellung) in Maschinencode zu kompilieren?

Vermutlich müsste eine Verknüpfung zu einer Python-Laufzeitbibliothek hergestellt werden, und alle Teile der Python-Standardbibliothek, die Python selbst waren, müssten ebenfalls kompiliert (und verlinkt) werden.

Sie müssen den Python-Interpreter auch bündeln, wenn Sie eine dynamische Auswertung von Ausdrücken durchführen möchten. Möglicherweise ist jedoch eine Teilmenge von Python, die dies nicht zulässt, dennoch nützlich.

Würde es Vorteile in Bezug auf Geschwindigkeit und/oder Speicherauslastung bieten? Vermutlich würde die Startzeit des Python-Interpreters wegfallen (obwohl Shared Libraries beim Start noch geladen werden müssten).

115
Andy Balaam

Probieren Sie ShedSkin Python-to-C++ - Compiler aus, aber er ist alles andere als perfekt. Es gibt auch Psyco - Python JIT, wenn nur Beschleunigung erforderlich ist. Aber meiner Meinung nach lohnt sich der Aufwand nicht. Für geschwindigkeitskritische Teile des Codes wäre es die beste Lösung, sie als C/C++ - Erweiterungen zu schreiben. 

22
cleg

Wie @Greg Hewgill sagt, gibt es gute Gründe, warum dies nicht immer möglich ist. Bestimmte Arten von Code (wie sehr algorithmischer Code) können jedoch in "echten" Maschinencode umgewandelt werden. 

Es gibt mehrere Möglichkeiten:

  • Verwenden Sie Psyco , wodurch der Maschinencode dynamisch ausgegeben wird. Sie sollten jedoch sorgfältig auswählen, welche Methoden/Funktionen konvertiert werden sollen.
  • Verwenden Sie Cython , eine Python -like -Sprache, die in eine Python C-Erweiterung kompiliert wird
  • Verwenden Sie PyPy , das einen Übersetzer von RPython (ein eingeschränktes Subset von Python, der einige der "dynamischsten" Funktionen von Python nicht unterstützt) für C oder LLVM verwendet.
    • PyPy ist immer noch sehr experimentell
    • es werden nicht alle Erweiterungen vorhanden sein

Danach können Sie eines der vorhandenen Pakete (Freeze, Py2exe, PyInstaller) verwenden, um alles in eine Binärdatei zu packen.

Alles in allem: Es gibt keine generelle Antwort auf Ihre Frage. Wenn Sie über leistungskritischen Python-Code verfügen, versuchen Sie, so viele integrierte Funktionen wie möglich zu verwenden (oder stellen Sie die Frage "Wie mache ich meinen Python-Code schneller"). Wenn dies nicht hilft, versuchen Sie, den Code zu identifizieren, portieren Sie ihn nach C (oder Cython) und verwenden Sie die Erweiterung.

46
Torsten Marek

py2c ( http://code.google.com/p/py2c ) kann Python-Code in c/c ++ .__ konvertieren. Ich bin der Einzelentwickler von py2c.

16
Ramchandra Apte

Nuitka ist ein Python-zu-C++ - Compiler, der Links gegen Libpython herstellt. Es scheint ein relativ neues Projekt zu sein. Der Autor behauptet eine Geschwindigkeitsverbesserung über CPython beim Pystone-Benchmark. 

13
bcattle

PyPy ist ein Projekt zur Reimplementierung von Python in Python, wobei Kompilierung zu nativem Code als eine der Implementierungsstrategien verwendet wird (andere sind ein VM mit JIT, JVM usw.). Ihre kompilierten C-Versionen laufen im Durchschnitt langsamer als CPython, aber für einige Programme viel schneller.

Shedskin ist ein experimenteller Python-to-C++ - Compiler.

Pyrex ist eine Sprache, die speziell für das Schreiben von Python-Erweiterungsmodulen entwickelt wurde. Es wurde entwickelt, um die Lücke zwischen der netten, einfach zu benutzenden Welt von Python und der unordentlichen, niedrigen Welt von C zu schließen.

13
pdc

Pyrex ist eine Teilmenge der Python-Sprache, die nach C kompiliert wird. Dies wird von dem Mann erledigt, der zuerst Listenverständnisse für Python erstellt hat. Es wurde hauptsächlich für Gebäudeumhüllungen entwickelt, kann aber in einem allgemeineren Kontext verwendet werden. Cython ist eine aktivere gepflegte Gabel von Pyrex.

Dies mag auf den ersten Blick vernünftig erscheinen, jedoch gibt es in Python viele gewöhnliche Dinge, die nicht direkt einer C-Repräsentation zugeordnet werden können, ohne viel Unterstützung für die Python-Laufzeitumgebung zu übernehmen. Beispielsweise fällt Ihnen das Schreiben von Ente ein. Viele Funktionen in Python, die Eingaben lesen, können ein - oder dateiähnliches -Objekt enthalten, sofern sie bestimmte Operationen unterstützen, z. read () oder readline (). Wenn Sie darüber nachdenken, was für die Zuordnung dieser Art von Unterstützung zu C erforderlich ist, beginnen Sie, sich genau die Art von Dingen vorzustellen, die das Python-Laufzeitsystem bereits tut.

Es gibt Dienstprogramme wie py2exe , die ein Python-Programm und die Laufzeit in eine einzige ausführbare Datei (soweit möglich) bündeln.

9
Greg Hewgill

Einige zusätzliche Referenzen:

5

Jython hat einen Compiler, der auf JVM-Bytecode abzielt. Der Bytecode ist voll dynamisch, genau wie die Python-Sprache selbst! Sehr cool. (Ja, da Greg Hewgills Antwort anspielt, verwendet der Bytecode die Jython-Laufzeitumgebung, sodass die Jython-JAR-Datei mit Ihrer App verteilt werden muss.)

3

Psyco ist eine Art Just-in-Time-Compiler (JIT-Compiler): Dynamischer Compiler für Python, der Code 2-100-mal schneller ausführt, aber viel Speicher benötigt.

Kurz gesagt: Sie führen Ihre vorhandene Python-Software viel schneller aus, ohne dass sich der Quellcode ändert, der Objektcode jedoch nicht auf die gleiche Weise kompiliert wird wie ein C-Compiler.

2

Die Antwort lautet "Ja, es ist möglich". Sie können Python-Code verwenden und versuchen, ihn mithilfe der CPython-API in den entsprechenden C-Code zu kompilieren. Tatsächlich gab es früher ein Python2C-Projekt, das genau das tat, aber ich habe seit vielen Jahren nichts davon gehört (in der Python-Zeit von 1,5 Tagen war es das letzte Mal, als ich es gesehen habe.)

Sie können versuchen, den Python-Code so viel wie möglich in natives C zu übersetzen, und auf die CPython-API zurückgreifen, wenn Sie echte Python-Funktionen benötigen. Ich habe die letzten ein oder zwei Monate mit dieser Idee gespielt. Es ist jedoch sehr viel Arbeit, und eine enorme Menge an Python-Funktionen ist sehr schwer in C zu übersetzen: verschachtelte Funktionen, Generatoren, alles andere als einfache Klassen mit einfachen Methoden, alles, was das Modifizieren von Modul-Globals von außerhalb des Moduls usw. betrifft , usw.

2
Thomas Wouters

Dies konvertiert Python nicht in Maschinencode. Erlaubt jedoch das Erstellen einer gemeinsam genutzten Bibliothek zum Aufrufen von Python-Code.

Wenn Sie danach suchen, können Sie auf einfache Weise Python-Code von C aus ausführen, ohne sich auf Execp verlassen zu müssen. Sie könnten eine gemeinsam genutzte Bibliothek aus Python-Code generieren, der mit ein paar Aufrufen der Python-Einbettungs-API umbrochen wird. Nun, die Anwendung ist eine gemeinsam genutzte Bibliothek, eine .so, die Sie in vielen anderen Bibliotheken/Anwendungen verwenden können.

Hier ist ein einfaches Beispiel, bei dem Sie eine gemeinsam genutzte Bibliothek erstellen, die Sie mit einem C-Programm verknüpfen können. Die gemeinsam genutzte Bibliothek führt Python-Code aus.

Die Python-Datei, die ausgeführt wird, ist 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

Sie können es mit python2 -c "import pythoncalledfromc; pythoncalledfromc.main('HELLO') versuchen. Es wird ausgegeben:

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

Die gemeinsam genutzte Bibliothek wird durch callpython.h folgendermaßen definiert:

#ifndef CALL_PYTHON
#define CALL_PYTHON

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

#endif

Der zugehörige callpython.c lautet:

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

Sie können es mit dem folgenden Befehl kompilieren:

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

Erstellen Sie eine Datei mit dem Namen callpythonfromc.c, die Folgendes enthält:

#include "callpython.h"

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

Kompilieren Sie es und führen Sie Folgendes aus:

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

Dies ist ein sehr einfaches Beispiel. Es kann funktionieren, aber abhängig von der Bibliothek kann es immer noch schwierig sein, C-Datenstrukturen in Python und von Python in C zu serialisieren. Die Dinge lassen sich etwas automatisieren ...

Nuitka könnte hilfreich sein.

Es gibt auch numba , aber beide wollen nicht genau das machen, was Sie wollen. Das Generieren eines C-Headers aus Python-Code ist möglich, jedoch nur, wenn Sie angeben, wie die Python-Typen in C-Typen konvertiert werden sollen, oder auf diese Informationen zurückgreifen können. Siehe Python Astroid für einen Python Ast Analyzer.

2
amirouche
0
tav