¡Hola, Habr!
Quiero contarles sobre un evento increíble del que me enteré hace un par de meses. Resulta que una popular utilidad de Python se ha distribuido durante más de un año como binarios que se compilan directamente desde Python. Y no se trata de un empaquetado banal por parte de algún PyInstaller , sino de una compilación honesta y anticipada de todo el paquete de Python. Si estás tan sorprendido como yo, bienvenido a cat.
Permítanme explicarles por qué creo que este evento es realmente asombroso. Hay dos tipos de compilación: anticipada (AOT) , cuando todo el código se compila antes de iniciar el programa, y compilador justo a tiempo (JIT) , cuando el programa se compila directamente para la arquitectura de procesador requerida durante su ejecución. . En el segundo caso, el lanzamiento inicial del programa lo realiza una máquina virtual o intérprete.
Si agrupamos los lenguajes de programación populares por tipo de compilación, obtenemos la siguiente lista:
Compilador adelantado: C, C ++, Rust, Kotlin, Nim, D, Go, Dart;
Compilador justo a tiempo: Lua, C #, Groovy, Dart.
No hay un compilador JIT listo para usar en Python, pero las bibliotecas separadas que brindan esa oportunidad han existido durante mucho tiempo
, : . : Kotlin JIT JavaVM, AOT Kotlin/Native. Dart ( 2). A JIT-, .
, , . .
JIT , . JIT . AOT , ? , .
, , . mypy - python-.
2019 , . — mypyc. , “ 4 Python-” mypy ( : 1, 2, 3). mypyc: mypy python- Dropbox, , . , : go cython. — AOT python-.
, mypy , . mypy “” python, , mypyc .
, , python-. Python c 3.4 , mypy . , python , AOT . , mypyc !
bubble_sort
“”. lib.py:
def bubble_sort(data):
n = len(data)
for i in range(n - 1):
for j in range(n - i - 1):
if data[j] > data[j + 1]:
buff = data[j]
data[j] = data[j + 1]
data[j + 1] = buff
return data
, mypyc . , mypyc. , mypy, mypyc ! mypyc, :
> mypyc lib.py
:
.mypy_cache
— mypy , mypyc mypy AST;
build
— ;
lib.cpython-38-x86_64-linux-gnu.so
— . CPython Extension.
CPython Extension — CPython , /C++. , CPython lib. , python.
:
python ;
.so , mypyc gcc (gcc python-dev ).
lib.cpython-38-x86_64-linux-gnu.so
lib.py , .
. main.py :
import lib
data = lib.bubble_sort(list(range(5000, 0, -1)))
assert data == list(range(1, 5001))
:
|
|
real 5.68 user 5.60 sys 0.01 |
real 2.78 user 2.73 sys 0.01 |
(~ 2 ), , . .
“ ”, . , .
sum(a, b)
:
def sum(a, b):
return a + b
:
int sum(int a, int b) {
return a + b;
}
c ( ):
PyObject *CPyDef_sum(PyObject *cpy_r_a, PyObject *cpy_r_b){
return PyNumber_Add(cpy_r_a, cpy_r_b);
}
, . -, , PyObject, CPython . , , : , , , , . mypyc?
, : CPython . PyNumber_Add — Python, , Python .
CPython c Extension :
— - sum A, B;
— , , A + B;
— ;
— , - .
: , , .
, , , mypyc , .
sum(a: int, b: int)
, python, , , . , . , CPython - Extension. ?
, , , , CPython. mypyc , . mypyc , , sum. , , . , -, :
def sum(a: int, b: int):
return a + b
C ( ):
PyObject *CPyDef_sum(CPyTagged cpy_r_a, CPyTagged cpy_r_b) {
CPyTagged cpy_r_r0;
PyObject *cpy_r_r1;
cpy_r_r0 = CPyTagged_Add(cpy_r_a, cpy_r_b);
cpy_r_r1 = CPyTagged_StealAsObject(cpy_r_r0);
return cpy_r_r1;
}
, : , , . , .
CPyDef_sum PyObject, CPyTagged. int, CPython, mypyc, . , sum int .
CPyTaggetAdd PyNumber_Add. mypyc. CPyTaggetAdd, , a b, int, , :
if (likely(CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right))) {
CPyTagged sum = left + right;
if (likely(!CPyTagged_IsAddOverflow(sum, left, right))) {
return sum;
}
}
, CPython - Extension :
— - sum A, B;
— , .
bubble_sort(data: List[int])
, . , data:
def bubble_sort(data: List[int]):
…
:
|
, |
|
real 5.68 user 5.60 sys 0.01 |
real 2.78 user 2.73 sys 0.01 |
real 1.32 user 1.30 sys 0.01 |
, , , !
mypyc
, , . mypyc : , , , mypy. mypy . python-, , .
, , :
;
monkey patching;
Mypy , .
. , , abc. , . , gcc , , , . , , 20 % , .
Nuitka
, . Nuitka . , Nuitka Python ++ , Python Extension. , CPython libpython.
Nuitka , . mypy .
, mypy : , “ ”, PyCharm . , mypy. , . , python. mypy — , . , CPython , , . (, mypyc ). , mypyc , , , - , mypyc, , , mypy.
P.S.
, python, . , mypyc, , , .
UPD
, python - Cython, python ( cython-). cython , (real 1.82) mypyc . .