¿Es factible compilar Python al código de máquina?

128

¿Qué tan factible sería compilar Python (posiblemente a través de una representación C intermedia) en código máquina?

Presumiblemente, necesitaría vincularse a una biblioteca de tiempo de ejecución de Python, y cualquier parte de la biblioteca estándar de Python que fuera Python también necesitaría compilarse (y vincularse).

Además, necesitaría agrupar el intérprete de Python si quisiera hacer una evaluación dinámica de las expresiones, pero quizás un subconjunto de Python que no lo permitiera aún sería útil.

¿Proporcionaría alguna ventaja de velocidad y / o uso de memoria? Presumiblemente, el tiempo de inicio del intérprete de Python se eliminaría (aunque las bibliotecas compartidas aún tendrían que cargarse al inicio).

Andy Balaam
fuente
2
Por cierto, su pregunta sería en mi humilde opinión sería más clara si solicita "código de máquina" en lugar de código de objeto.
Torsten Marek

Respuestas:

31

Pruebe el compilador ShedSkin Python-to-C ++, pero está lejos de ser perfecto. También hay Psyco - Python JIT si solo se necesita acelerar. Pero en mi humilde opinión esto no vale la pena el esfuerzo. Para las partes de código de velocidad crítica, la mejor solución sería escribirlas como extensiones C / C ++.

tábano
fuente
55
Para su información, ShedSkin dejó de admitir Windows.
sorin
2
@sorin: bueno, hoy es compatible con Windows ... code.google.com/p/shedskin/downloads/…
2
La mejor solución, speedwise, aún podría ser PyPy .
Cees Timmerman
Shedskin no ha trabajado en eso en aproximadamente dos años. :(
Perkins
53

Como lo dice @Greg Hewgill, hay buenas razones por las cuales esto no siempre es posible. Sin embargo, ciertos tipos de código (como el código muy algorítmico) pueden convertirse en código de máquina "real".

Hay varias opciones:

  • Use Psyco , que emite código de máquina dinámicamente. Sin embargo, debe elegir cuidadosamente qué métodos / funciones convertir.
  • Use Cython , que es un lenguaje similar a Python que se compila en una extensión Python C
  • Utilice PyPy , que tiene un traductor de RPython (un subconjunto restringido de Python que no admite algunas de las características más "dinámicas" de Python) a C o LLVM.
    • PyPy sigue siendo altamente experimental
    • no todas las extensiones estarán presentes

Después de eso, puede usar uno de los paquetes existentes (congelar, Py2exe, PyInstaller) para poner todo en un binario.

En general: no hay una respuesta general para su pregunta. Si tiene un código Python que es crítico para el rendimiento, intente utilizar la mayor cantidad de funcionalidades incorporadas posible (o pregunte "¿Cómo hago que mi código Python sea más rápido?"). Si eso no ayuda, intente identificar el código y transferirlo a C (o Cython) y use la extensión.

Torsten Marek
fuente
3
PyPy es el sucesor de Psyco
bcattle
19

py2c ( https://github.com/pradyun/Py2C ) puede convertir el código de Python a c / c ++ Soy el desarrollador en solitario de py2c.

Ramchandra Apte
fuente
Esto parece una herramienta útil. ¿Sigue siendo mantenido?
Anderson Green
@AndersonGreen Está en una etapa temprana de desarrollo la última vez que estuve trabajando en ello (probablemente similar ahora). Dejé el proyecto porque soy ̶b̶u̶s̶y̶ soy vago. Si no ha notado el texto "Importante", se ha movido a GitHub ahora.
Ramchandra Apte
El enlace apunta a un instalador no vencido , que parece ser un proyecto diferente. ¿Py2c todavía está disponible en GitHub?
Anderson Green
@AndersonGreen ¡Guau, eso pasó desapercibido durante tanto tiempo! Aquí tienes
Ramchandra Apte
El enlace en code.google.com/p/py2c todavía apunta a un instalador no vencido, por lo que debe actualizarse ahora.
Anderson Green
15

PyPy es un proyecto para reimplementar Python en Python, usando la compilación en código nativo como una de las estrategias de implementación (otras son una VM con JIT, usando JVM, etc.). Sus versiones C compiladas funcionan más lento que CPython en promedio, pero mucho más rápido para algunos programas.

Shedskin es un compilador experimental de Python a C ++.

Pyrex es un lenguaje especialmente diseñado para escribir módulos de extensión de Python. Está diseñado para cerrar la brecha entre el mundo agradable, de alto nivel y fácil de usar de Python y el mundo desordenado y de bajo nivel de C.

pdc
fuente
3
Cython es la bifurcación amigable más utilizada y desarrollada de forma más activa de Pyrex.
Mike Graham
"El mundo agradable, de alto nivel y fácil de usar de Python y el mundo desordenado y de bajo nivel de C" - gracioso, solo estaba pensando en cómo C y el ensamblador son "agradables" y simples, y Python vive en el " mundo desordenado "de alto nivel"
Ingeniero invertido
14

Nuitka es un compilador de Python a C ++ que enlaza contra libpython. Parece ser un proyecto relativamente nuevo. El autor afirma una mejora de la velocidad sobre CPython en el punto de referencia de pystone.

bcattle
fuente
10

Esto puede parecer razonable a primera vista, sin embargo, hay muchas cosas comunes en Python que no se pueden mapear directamente a una representación en C sin llevar mucho soporte de tiempo de ejecución de Python. Por ejemplo, mecanografía a los patos viene a la mente. Muchas funciones en Python que leen la entrada pueden tomar un archivo o un objeto similar a un archivo , siempre que admita ciertas operaciones, por ejemplo. read () o readline (). Si piensa en lo que se necesitaría para asignar este tipo de soporte a C, comenzará a imaginar exactamente el tipo de cosas que el sistema de ejecución Python ya hace.

Hay utilidades como py2exe que agruparán un programa Python y tiempo de ejecución en un solo ejecutable (en la medida de lo posible).

Greg Hewgill
fuente
1
¿Qué sucede si mi objetivo era asegurarme de que el código se compila, porque los lenguajes compilados estáticamente son (al menos en mi opinión) menos propensos a explotar en tiempo de ejecución? ¿Es posible determinar que alguna foo.xexpresión no funcionará porque foono tendrá xen el momento en que se llama? ¿Hay algún corrector de código estático para Python? Python se puede compilar en un ensamblado .Net ...
Hamish Grubijan
10

Pyrex es un subconjunto del lenguaje Python que se compila en C, hecho por el tipo que primero creó las comprensiones de listas para Python. Fue desarrollado principalmente para construir envoltorios, pero puede usarse en un contexto más general. Cython es una bifurcación de pyrex más activamente mantenida.

Preocupado por TunbridgeWells
fuente
2
Cython es la bifurcación amigable más utilizada y desarrollada de forma más activa de Pyrex.
Mike Graham
3

Jython tiene un compilador dirigido a bytecode JVM. ¡El bytecode es completamente dinámico, al igual que el lenguaje Python mismo! Muy genial. (Sí, como alude la respuesta de Greg Hewgill, el código de byte usa el tiempo de ejecución de Jython, por lo que el archivo jar de Jython debe distribuirse con su aplicación).

Chris Jester-Young
fuente
2

Psyco es una especie de compilador justo a tiempo (JIT): compilador dinámico para Python, ejecuta código de 2 a 100 veces más rápido, pero necesita mucha memoria.

En resumen: ejecuta su software Python existente mucho más rápido, sin cambios en su fuente, pero no compila el código objeto de la misma manera que lo haría un compilador de C.

Pierre-Jean Coudert
fuente
2

La respuesta es "Sí, es posible". Puede tomar el código Python e intentar compilarlo en el código C equivalente utilizando la API CPython. De hecho, solía haber un proyecto Python2C que hacía exactamente eso, pero no he oído hablar de él en muchos años (en los últimos 1.5 días en Python es cuando lo vi por última vez).

Podría intentar traducir el código de Python a C nativo tanto como sea posible, y recurrir a la API de CPython cuando necesite funciones reales de Python. He estado jugando con esa idea yo mismo el último mes o dos. Sin embargo, es una gran cantidad de trabajo, y una enorme cantidad de características de Python son muy difíciles de traducir a C: funciones anidadas, generadores, cualquier cosa menos clases simples con métodos simples, cualquier cosa que implique la modificación de módulos globales desde fuera del módulo, etc. etc.

Thomas Wouters
fuente
2

Esto no compila Python al código de la máquina. Pero permite crear una biblioteca compartida para llamar al código Python.

Si lo que está buscando es una manera fácil de ejecutar código Python desde C sin depender de cosas de execp. Podría generar una biblioteca compartida a partir del código de Python envuelto con algunas llamadas a la API de incrustación de Python . Bueno, la aplicación es una biblioteca compartida, un archivo .so que puedes usar en muchas otras bibliotecas / aplicaciones.

Aquí hay un ejemplo simple que crea una biblioteca compartida, que puede vincular con un programa en C. La biblioteca compartida ejecuta el código Python.

El archivo de Python que se ejecutará es 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

Puedes probarlo con python2 -c "import pythoncalledfromc; pythoncalledfromc.main('HELLO'). Producirá:

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

La biblioteca compartida se definirá de la siguiente manera callpython.h:

#ifndef CALL_PYTHON
#define CALL_PYTHON

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

#endif

El asociado callpython.ces:

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

Puedes compilarlo con el siguiente comando:

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

Cree un archivo llamado callpythonfromc.cque contenga lo siguiente:

#include "callpython.h"

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

Compílalo y ejecuta:

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

Este es un ejemplo muy básico. Puede funcionar, pero dependiendo de la biblioteca, podría ser difícil serializar estructuras de datos C a Python y de Python a C. Las cosas pueden automatizarse de alguna manera ...

Nuitka podría ser útil.

También hay numba, pero ambos no tienen como objetivo hacer exactamente lo que quieres. Es posible generar un encabezado C a partir del código Python, pero solo si especifica cómo convertir los tipos Python a tipos C o puede inferir esa información. Ver python astroid para un analizador Python ast.

amirouche
fuente