Rust es un lenguaje de programación de sistemas que se ejecuta rápidamente rápido, previene Segfaults y garantiza la seguridad de los hilos.
Presentado
Tomado de: de Rust-Lang.org
La mejor descripción del óxido escuché de Elias un miembro y el gurú de óxido del grupo de telegrama de Brasil Rust Brasil
El óxido es un lenguaje que le permite construir abstracciones de alto nivel, pero sin renunciar al control de bajo nivel, es decir, el control de cómo se representan los datos en la memoria, control de qué modelo de roscado desea usar, etc.
El óxido es un lenguaje que generalmente puede detectar, durante la compilación, el peor paralelismo y los errores de gestión de la memoria (como acceder a datos en diferentes hilos sin sincronización, o usar datos después de que se hayan desanimado), pero le da un escape de escotilla en caso de que realmente sepa lo que está haciendo.
El óxido es un idioma que, porque no tiene tiempo de ejecución, se puede usar para integrarse con cualquier tiempo de ejecución; Puede escribir una extensión nativa en Rust que llama un programa Node.js, o por un programa de Python, o por un programa en Ruby, Lua, etc. y, por otro lado, puede escribir un programa en óxido usando estos idiomas. - "Elias Gabriel Amaral da Silva"

Hay un montón de paquetes de óxido para ayudarlo a extender Python con óxido.
Puedo mencionar la lechería creada por Armin Ronacher (el Creador de Flask) y también Pyo3 The Rustingings para Python Interpreter
Vea una lista completa de referencias en la parte inferior.
Para esta publicación, voy a usar Rust Cpython, es el único que he probado, es compatible con la versión estable de Rust y me encontré directamente de usar.
Nota : PYO3 es una bifurcación de Rust-Cpython, viene con muchas mejoras, pero solo funciona con la versión nocturna de Rust, por lo que preferí usar el establo para esta publicación, de todos modos, los ejemplos aquí deben funcionar también con PYO3.
Pros: es realmente fácil escribir funciones de óxido e importar desde Python y, como verá, por los puntos de referencia que vale en términos de rendimiento.
Contras: La distribución de su proyecto/lib/marco exigirá que el módulo de óxido se compile en el sistema de destino debido a la variación del entorno y la arquitectura, habrá una etapa de compilación que no tiene al instalar bibliotecas de Python puro, puede facilitar el uso de óxido-setuptools o usar la Milksnake a los datos binarios incrustados en los wheels de Python.
Sí, Python es conocido por ser "lento" en algunos casos y la buena noticia es que esto realmente no importa dependiendo de los objetivos y prioridades de su proyecto. Para la mayoría de los proyectos, este detalle no será muy importante.
Sin embargo, puede enfrentar el caso raro en el que una sola función o módulo está tomando demasiado tiempo y se detecta como el cuello de botella del rendimiento de su proyecto, a menudo ocurre con el análisis de cadenas y el procesamiento de imágenes.
Digamos que tiene una función de Python que realiza algún tipo de procesamiento de cadenas, tome el siguiente ejemplo fácil de counting pairs of repeated chars pero tiene en cuenta que este ejemplo puede reproducirse con otras funciones string processing o cualquier otro proceso generalmente lento en Python.
# How many subsequent-repeated group of chars are in the given string?
abCCdeFFghiJJklmnopqRRstuVVxyZZ... {millions of chars here}
1 2 3 4 5 6 Python es bastante lento para hacer un procesamiento string grandes, por lo que puede usar pytest-benchmark para comparar una función Pure Python (with Iterator Zipping) versus una implementación Regexp .
# Using a Python3.6 environment
$ pip3 install pytest pytest-benchmark
Luego escriba un nuevo programa de Python llamado doubles.py
import re
import string
import random
# Python ZIP version
def count_doubles ( val ):
total = 0
for c1 , c2 in zip ( val , val [ 1 :]):
if c1 == c2 :
total += 1
return total
# Python REGEXP version
double_re = re . compile ( r'(?=(.)1)' )
def count_doubles_regex ( val ):
return len ( double_re . findall ( val ))
# Benchmark it
# generate 1M of random letters to test it
val = '' . join ( random . choice ( string . ascii_letters ) for i in range ( 1000000 ))
def test_pure_python ( benchmark ):
benchmark ( count_doubles , val )
def test_regex ( benchmark ):
benchmark ( count_doubles_regex , val )Ejecute Pytest para comparar:
$ pytest doubles.py
=============================================================================
platform linux -- Python 3.6.0, pytest-3.2.3, py-1.4.34, pluggy-0.4.
benchmark: 3.1.1 (defaults: timer=time.perf_counter disable_gc=False min_roun
rootdir: /Projects/rustpy, inifile:
plugins: benchmark-3.1.1
collected 2 items
doubles.py ..
-----------------------------------------------------------------------------
Name (time in ms) Min Max Mean
-----------------------------------------------------------------------------
test_regex 24.6824 (1.0) 32.3960 (1.0) 27.0167 (1.0)
test_pure_python 51.4964 (2.09) 62.5680 (1.93) 52.8334 (1.96)
-----------------------------------------------------------------------------
Tomemos la Mean para la comparación:
Crate es cómo llamamos paquetes de óxido.
Tener Rust Installed (la forma recomendada es https://www.rustup.rs/) Rust también está disponible en Fedora y Rhel Rust-Toolset
Usé
rustc 1.21.0
En la misma carpeta Ejecutar:
cargo new pyext-myrustlib Crea un nuevo proyecto de óxido en la src/lib.rs carpeta llamada pyext-myrustlib que contiene la Cargo.toml .
Utilizará la caja rust-cpython como dependencia y le dirá a la carga que genere un dylib para importar desde Python
[ package ]
name = " pyext-myrustlib "
version = " 0.1.0 "
authors = [ " Bruno Rocha <[email protected]> " ]
[ lib ]
name = " myrustlib "
crate-type = [ " dylib " ]
[ dependencies . cpython ]
version = " 0.1 "
features = [ " extension-module " ]Lo que necesitamos hacer:
Importar todas las macros de cpython Crate
Tome los tipos Python y PyResult de Cpython en nuestro alcance de Lib
Escriba la implementación de la función count_doubles en Rust , tenga en cuenta que esto es muy similar a la versión Pure Python, excepto:
Python como primer argumento, que es una referencia al intérprete de Python y permite que el óxido use el Python GIL&str escribió val como referenciaPyResult que es un tipo que permite el aumento de las excepciones de PythonPyResult en Ok(total) ( el resultado es un tipo de enum que representa el éxito (OK) o la falla (err)) y, como se espera que nuestra función devuelva un PyResult , el compilador se encargará de envolver nuestro Ok en ese tipo. (Tenga en cuenta que nuestro PyResult espera un u64 como valor de retorno) ¡Usando py_module_initializer! Macro registramos nuevos atributos a la lib, incluido el __doc__ y también agregamos el atributo count_doubles que hace referencia a nuestra Rust implementation of the function
try! macro que es equivalente al try.. exceptOk(()) - El () es una tupla de resultado vacía, el equivalente de None en Python # [ macro_use ]
extern crate cpython ;
use cpython :: { Python , PyResult } ;
fn count_doubles ( _py : Python , val : & str ) -> PyResult < u64 > {
let mut total = 0u64 ;
for ( c1 , c2 ) in val . chars ( ) . zip ( val . chars ( ) . skip ( 1 ) ) {
if c1 == c2 {
total += 1 ;
}
}
Ok ( total )
}
py_module_initializer ! ( libmyrustlib , initlibmyrustlib , PyInit_myrustlib , | py , m | {
try ! ( m . add ( py , " __doc__ " , " This module is implemented in Rust " ) ) ;
try! ( m . add ( py , " count_doubles " , py_fn ! ( py , count_doubles ( val : & str ) ) ) ) ;
Ok ( ( ) )
} ) ;Ahora vamos a construirlo en carga
$ cargo build --release
Finished release [optimized] target(s) in 0.0 secs
$ ls -la target/release/libmyrustlib *
target/release/libmyrustlib.d
target/release/libmyrustlib.so * < -- Our dylib is here Ahora copiemos la generada .so Lib a la misma carpeta donde nuestros doubles.py .
Nota: En Fedora debe obtener un
.soen otro sistema, puede obtener un.dyliby puede cambiar el nombre de la extensión cambiante a.so
$ cd ..
$ ls
doubles.py pyext-myrustlib/
$ cp pyext-myrustlib/target/release/libmyrustlib.so myrustlib.so
$ ls
doubles.py myrustlib.so pyext-myrustlib/Tener el
myrustlib.soEn la misma carpeta o agregar a su ruta de Python le permite importar directamente, transparentemente, ya que era un módulo Python.
Edite sus doubles.py ahora importando nuestra versión Rust implemented y también agregue un benchmark para ella.
import re
import string
import random
import myrustlib # <-- Import the Rust implemented module (myrustlib.so)
def count_doubles ( val ):
"""Count repeated pair of chars ins a string"""
total = 0
for c1 , c2 in zip ( val , val [ 1 :]):
if c1 == c2 :
total += 1
return total
double_re = re . compile ( r'(?=(.)1)' )
def count_doubles_regex ( val ):
return len ( double_re . findall ( val ))
val = '' . join ( random . choice ( string . ascii_letters ) for i in range ( 1000000 ))
def test_pure_python ( benchmark ):
benchmark ( count_doubles , val )
def test_regex ( benchmark ):
benchmark ( count_doubles_regex , val )
def test_rust ( benchmark ): # <-- Benchmark the Rust version
benchmark ( myrustlib . count_doubles , val )$ pytest doubles.py
==============================================================================
platform linux -- Python 3.6.0, pytest-3.2.3, py-1.4.34, pluggy-0.4.
benchmark: 3.1.1 (defaults: timer=time.perf_counter disable_gc=False min_round
rootdir: /Projects/rustpy, inifile:
plugins: benchmark-3.1.1
collected 3 items
doubles_rust.py ...
-----------------------------------------------------------------------------
Name (time in ms) Min Max Mean
-----------------------------------------------------------------------------
test_rust 2.5555 (1.0) 2.9296 (1.0) 2.6085 (1.0)
test_regex 25.6049 (10.02) 27.2190 (9.29) 25.8876 (9.92)
test_pure_python 52.9428 (20.72) 56.3666 (19.24) 53.9732 (20.69)
----------------------------------------------------------------------------- Tomemos la Mean para la comparación:
La implementación de la óxido puede ser 10 veces más rápida que Python Regex y 21x más rápido que la versión de Python Pure.
Es interesante que la versión regex sea solo 2 veces más rápido que la python pura :)
Nota: Esos números tienen sentido solo para este escenario particular, para otros casos que la comparación puede ser diferente.
Después de que se haya publicado este artículo, recibí algunos comentarios sobre R/Python y también sobre R/Rust
Las contribuciones vienen como solicitudes de extracción y puede enviar un nuevo si cree que las funciones pueden mejorarse.
Gracias a: Josh Stone obtuvimos una mejor implementación para Rust que itera la cuerda solo una vez y también el equivalente de Python.
Gracias a: Purple Pixie obtuvimos una implementación de Python usando itertools , sin embargo, esta versión no está funcionando mejor, necesita mejoras.
fn count_doubles_once ( _py : Python , val : & str ) -> PyResult < u64 > {
let mut total = 0u64 ;
let mut chars = val . chars ( ) ;
if let Some ( mut c1 ) = chars . next ( ) {
for c2 in chars {
if c1 == c2 {
total += 1 ;
}
c1 = c2 ;
}
}
Ok ( total )
} def count_doubles_once ( val ):
total = 0
chars = iter ( val )
c1 = next ( chars )
for c2 in chars :
if c1 == c2 :
total += 1
c1 = c2
return total import itertools
def count_doubles_itertools ( val ):
c1s , c2s = itertools . tee ( val )
next ( c2s , None )
total = 0
for c1 , c2 in zip ( c1s , c2s ):
if c1 == c2 :
total += 1
return total Ok, ese no es el propósito de esta publicación, esta publicación nunca se trató de comparar Rust X other language , esta publicación se trataba específicamente de cómo usar Rust para extender y acelerar Python y, al hacerlo, significa que tiene una buena razón para elegir Rust en lugar de other language o por su ecosistema o por su seguridad y herramientas o simplemente seguir el Hype, o simplemente porque le gusta Rust no importa, esta publicación aquí para mostrar cómo usarlo con Pyton .
Yo (personalmente) puedo decir que Rust es más future proof , ya que es nueva y hay muchas mejoras por venir, también debido a su ecosistema, herramientas y comunidad y también porque me siento cómodo con la sintaxis de la óxido, ¡realmente me gusta!
Entonces, como se esperaba, las personas comenzaron a quejarse del uso de otros idiomas y se convierte en una especie de punto de referencia, ¡y creo que es genial!
Entonces, como parte de mi solicitud de mejoras, algunas personas en Hacker News también enviaron ideas, Martinxyz envió una implementación utilizando C y Swig que funcionó muy bien.
Código C (Swig Boilerplate ommited)
uint64_t count_byte_doubles ( char * str ) {
uint64_t count = 0 ;
while ( str [ 0 ] && str [ 1 ]) {
if ( str [ 0 ] == str [ 1 ]) count ++ ;
str ++ ;
}
return count ;
} Y nuestro compañero Red Hatter Josh Stone mejoró nuevamente la implementación de la óxido al reemplazar chars con bytes , por lo que es una competencia justa con C , ya que C está comparando bytes en lugar de Chars Unicode.
fn count_doubles_once_bytes ( _py : Python , val : & str ) -> PyResult < u64 > {
let mut total = 0u64 ;
let mut chars = val . bytes ( ) ;
if let Some ( mut c1 ) = chars . next ( ) {
for c2 in chars {
if c1 == c2 {
total += 1 ;
}
c1 = c2 ;
}
}
Ok ( total )
} También hay ideas para comparar list comprehension python y numpy así que incluí aquí
Numpy:
import numpy as np
def count_double_numpy ( val ):
ng = np . fromstring ( val , dtype = np . byte )
return np . sum ( ng [: - 1 ] == ng [ 1 :])Comprensión de la lista
def count_doubles_comprehension ( val ):
return sum ( 1 for c1 , c2 in zip ( val , val [ 1 :]) if c1 == c2 ) El caso de prueba completo está en el archivo Repository test_all.py .
-------------------------------------------------------------------------------------------------
Name (time in us) Min Max Mean
-------------------------------------------------------------------------------------------------
test_rust_bytes_once 476.7920 (1.0) 830.5610 (1.0) 486.6116 (1.0)
test_c_swig_bytes_once 795.3460 (1.67) 1,504.3380 (1.81) 827.3898 (1.70)
test_rust_once 985.9520 (2.07) 1,483.8120 (1.79) 1,017.4251 (2.09)
test_numpy 1,001.3880 (2.10) 2,461.1200 (2.96) 1,274.8132 (2.62)
test_rust 2,555.0810 (5.36) 3,066.0430 (3.69) 2,609.7403 (5.36)
test_regex 24,787.0670 (51.99) 26,513.1520 (31.92) 25,333.8143 (52.06)
test_pure_python_once 36,447.0790 (76.44) 48,596.5340 (58.51) 38,074.5863 (78.24)
test_python_comprehension 49,166.0560 (103.12) 50,832.1220 (61.20) 49,699.2122 (102.13)
test_pure_python 49,586.3750 (104.00) 50,697.3780 (61.04) 50,148.6596 (103.06)
test_itertools 56,762.8920 (119.05) 69,660.0200 (83.87) 58,402.9442 (120.02)
-------------------------------------------------------------------------------------------------
new Rust implementation comparing bytes es 2 veces mejor que el antiguo comparación chars UnicodeRust sigue siendo mejor que la C usando SwigRust que compara unicode chars sigue siendo mejor que numpyNumpy es mejor que la first Rust implementation que tuvo el problema de la doble iteración sobre los Chars Unicodelist comprehension no marca una diferencia significativa que el uso de pure PythonNota: Si desea proponer cambios o mejoras, envíe un PR aquí: https://github.com/rochacbruno/rust-python-example/
Recibí más contribuciones, ya que Jason Knight fue una de las Rust .
RUSTFLAGS= " -C target-cpu=native " cargo build --release Y para aquellos que tenían curiosidad por una comparación con numba , So Shyba lo implementaron y está disponible en la sucursal de Numba https://github.com/rochacbruno/rust-python-example/tree/numba.
from numba import jit
@ jit ( nopython = True , cache = True )
def count_doubles_once_numba ( val ):
total = 0
chars = iter ( val )
c1 = next ( chars )
for c2 in chars :
if c1 == c2 :
total += 1
c1 = c2
return totalMire los nuevos resultados con numba en la parte superior, bastante cerca de óxido
----------------------------------------------------------------------------------------------------
Name (time in us) Min Max Mean
----------------------------------------------------------------------------------------------------
test_pure_python_once_numba 292.0990 (1.0) 317.7590 (1.0) 296.7477 (1.0)
test_numpy_numba 326.2470 (1.12) 526.1350 (1.66) 338.1704 (1.14)
test_rust_bytes_once 336.0620 (1.15) 1,053.0090 (3.31) 342.5122 (1.15)
test_c_swig_bytes_once 375.6310 (1.29) 1,389.9070 (4.37) 388.9181 (1.31)
test_rust_once 986.0360 (3.38) 2,498.5850 (7.86) 1,006.5819 (3.39)
test_numpy 1,137.1750 (3.89) 2,000.5430 (6.30) 1,167.2551 (3.93)
test_rust 2,555.1400 (8.75) 3,645.3900 (11.47) 2,592.0419 (8.73)
test_regex 22,597.1750 (77.36) 25,027.2820 (78.76) 22,851.8456 (77.01)
test_pure_python_once 32,418.8830 (110.99) 34,818.0800 (109.57) 32,756.3244 (110.38)
test_pure_python 43,823.5140 (150.03) 45,961.8460 (144.64) 44,367.1028 (149.51)
test_python_comprehension 46,360.1640 (158.71) 50,578.1740 (159.17) 46,986.8058 (158.34)
test_itertools 49,080.8640 (168.03) 51,016.5230 (160.55) 49,405.2562 (166.49)
---------------------------------------------------------------------------------------------------- Y también hay una implementación de Cython de Mike Fletcher en la rama cython https://github.com/rochacbruno/rust-python-example/tree/cython
con los resultados:
----------------------------------------------------------------------------------------------------
Name (time in us) Min Max Mean
----------------------------------------------------------------------------------------------------
test_rust_bytes_once 336.7590 (1.0) 806.2610 (1.0) 346.5317 (1.0)
test_cython 756.1610 (2.25) 2,343.3680 (2.91) 785.6455 (2.27)
test_c_swig_bytes_once 802.4250 (2.38) 1,632.4290 (2.02) 840.8603 (2.43)
----------------------------------------------------------------------------------------------------De vuelta al propósito de esta publicación , cómo acelerar su pitón con óxido, comenzamos con:
En este ejemplo, Rust realizó 100 veces más rápido que nuestra Python pura .
Rust no lo salvará mágicamente, debe conocer el idioma para poder implementar la solución inteligente y una vez implementado en el derecho, vale tanto como C en términos de rendimiento y también viene con increíbles herramientas, ecosistemas, comunidad y bonos de seguridad.
Rust puede no ser el general purpose language de elección por su nivel de complejidad y puede no ser la mejor opción para escribir applications simples comunes, como sitios web y scripts test automation .
Sin embargo, para specific parts del proyecto donde se sabe que Python es el cuello de botella y su elección natural sería implementar una extensión C/C++ , escribir esta extensión en Rust parece fácil y mejor para mantener.
Todavía hay muchas mejoras para venir en óxido y muchas otras cajas para ofrecer integración Python <--> Rust . Incluso si no incluye el idioma en su cinturón de herramientas en este momento, ¡realmente vale la pena mantener un ojo abierto al futuro!
Los ejemplos en esta publicación se inspiran en Extending Python with Rust Talk de Samuel Cormier-Iijima en Pycon Canadá . Video aquí: https://www.youtube.com/watch?v=-ylbuezkg4m
Y también por My Python is a little Rust-y de Dan Callahan en Pycon Montreal . Video aquí: https://www.youtube.com/watch?v=3cwj0mh-4ma
Otras referencias:
Únete a la comunidad:
Únase a Rust Community, puede encontrar enlaces grupales en https://www.rust-lang.org/en-us/community.html
Si habla portugués, le recomiendo que se una a https://t.me/rustlangbr y también está http://bit.ly/canalrustbr en youtube.
Bruno Rocha
Más información: http://about.me/rochacbruno y http://brunorocha.org