Save 37% off PRO during our Black Friday Sale! »

Cuándo usar extensiones nativas en Rust: Rendimiento accesible y seguro

Cuándo usar extensiones nativas en Rust: Rendimiento accesible y seguro

9b20982cdfb8b3afee99d7dcf85b4b56?s=128

Eric Arellano

April 28, 2021
Tweet

Transcript

  1. Cuándo usar extensiones nativas en Rust: Rendimiento accesible y seguro

    Eric Arellano @EArellanoAZ PyCon US, Mayo 2021
  2. Plan de presentación 1. ¿Cuándo usar las extensiones nativas? 2.

    ¿Por qué usar Rust? 3. ¿Qué sigue de esto?
  3. Quién soy • Ingeniero para Toolchain Labs, Twitter, y Foursquare

    • Colaborador del proyecto Pants por 3 años • Consejero para jóvenes LGBTQ en crisis • Amante de cerdos
  4. Pants Una herramienta que coordina los builds. • ejecutar pruebas

    • empaqueter distribuciones • ejecutar linters • formatear código • generar Protobufs
  5. Pants - como usamos Rust + Python Python 3 para

    modelar toda la lógica de un build. Rust para la gestión de tareas, "the engine": • Lanzar procesos con concurrencia • Escribir/leer al caché • Observar el sistema de archivos para cambios
  6. 1. ¿Cuándo usar las extensiones nativas?

  7. Python suele ser lento "Why is Python slow?" Anthony Shaw

    Pycon 2020
  8. Pero no siempre es un problema "Why is Python slow?"

    Anthony Shaw Pycon 2020
  9. ¿Que importancia tiene el rendimiento para su proyecto?

  10. ¿Por qué CPython puede ser lento? Según "Why is Python

    Slow" (Anthony Shaw): • Compilación ◦ Hay una etapa de interpretación ◦ Bytecode no está siempre optimizado • Recolector de basura • Limites a la concurrencia ◦ GIL (global interpreter lock)
  11. Perfilar Característica de rendimiento Herramientas antes y después; CPU vs.

    I/O hyperfine, GNU time "hot spots" PySpy, "flame graphs" como Snakeviz gasto de memoria Guppy3
  12. Flame graphs

  13. Perfiles del GIL Es complicado porque hay que identificar la

    duración de espera. Tutorial en YouTube: "Identifying Contention on the Python GIL in Rust from macOS" por Pants Build, https://youtu.be/zALr3zFIQJo
  14. Algunas opciones cuando hay un problema de rendimiento 1. Optimizar.

    2. Probar la concurrencia de Python. 3. Probar PyPy o Numba. 4. Reescribir todo en otro lenguaje. 5. Usar las extensiones nativas.
  15. Opción 1: optimizar Por ejemplo: • Cambiar las estructuras de

    datos y algoritmos • Usar __slots__ para menos gasto de memoria Es bueno, pero hay limites fundamentales, como el GIL.
  16. Opción 2: la concurrencia de Python • multiprocessing ◦ evita

    el GIL, pero hay gastos • threading ◦ todavía tiene el GIL ◦ es útil cuando I/O-bound, no tanto cuando CPU-bound • asyncio http://masnun.rocks/2016/10/06/async-python-the-different-forms-of-concurrency/
  17. Opción 3 • Son intérpretes alternativos • Todavía escribe Python

    • PyPy es ~4.2x más rapido que CPython
  18. PyPy y Numba "If you want your code to run

    faster, you should probably just use PyPy." Guido van Rossum, creador de Python
  19. Opción 4: reescribir todo en otro lenguaje "The single worst

    strategic mistake that any software company can make: rewrite the code from scratch." Joel Spolsky, 2000 "Things You Should Never Do, Part 1"
  20. Reescribir todo en otro lenguaje Es mejor hacerlo incrementalmente.

  21. Opción 5: las extensiones nativas • Una mezcla de Python

    y otro lenguaje • FFI (Foreign Function Interface) • Importado como un módulo típico a través de un archivo .so
  22. Extensión - demo def suma_dos(x: int) -> int: return x

    + 2 if __name__ == "__main__": assert suma_dos(2) == 4 proyecto └── demo.py
  23. Extensión - demo from mi_extension_nativa import suma_dos if __name__ ==

    "__main__": assert suma_dos(2) == 4 proyecto ├── demo.py └── mi_extension_nativa.so
  24. Extensiones nativas Son muy populares • Cryptography • NumPy •

    Pandas • TensorFlow • PyYAML
  25. Cuándo las extensiones nativas pueden valer la pena • Optimizaciones

    y PyPy/Numpy no están suficientes • Hay "hot spots" mucho más rápidos con un lenguaje nativo ◦ Escribir experimentos sencillos para probar
  26. Por que Pants escogió una extensión nativa • Después de

    las optimizaciones, todavía gastaba 80% del tiempo en la gestión de tareas • Queriamos usar asyncio, pero con más control y sin el GIL • Tenemos estructuras de datos enormes -> control de la memoria
  27. Las extensiones nativas llevan complejidad • dos lenguajes para entender

    y mantener • empaquetamiento Menos riesgo con un límite claro de su interfaz.
  28. 2. ¿Por qué usar Rust?

  29. Algunas opciones para extensiones nativas • C ◦ ctypes/cffi ◦

    CPython API ◦ Cython* • C++ ◦ pybind11 • Rust ◦ Rust-CPython ◦ PyO3 • Golang *Cython es un dialecto de Python
  30. None
  31. Rust - demo fn suma_dos(x: i64) -> i64 { x

    + 2 } fn main() { println!("{}", suma_dos(2)); }
  32. Extensión - demo use pyo3::prelude::*; use pyo3::wrap_pyfunction; #[pyfunction] fn suma_two(x:

    i64) -> PyResult<i64> { Ok(x + 2) } #[pymodule] fn mi_extension_nativa(py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(suma_dos, m)?)?; Ok(()) }
  33. ¡Rust puede usar Python también! use pyo3::prelude::*; fn main() {

    Python::with_gil(|py| { let sys_module = py.import("sys").unwrap(); let version: String = sys_module .get("version") .unwrap() .extract() .unwrap(); println!("Usando Python {}", version); } }
  34. C y C++ no tienen la seguridad de memoria. La

    habilidad no elimina el riesgo.
  35. • ~70% de vulnerabilidades de Microsoft 1 • ~70% de

    vulnerabilidades severos de Android 2 • 74% de vulnerabilidades de Firefox Style Component 3 1. https://msrc-blog.microsoft.com/2019/07/18/we-need-a-safer-systems-programming-language/ 2. https://security.googleblog.com/2021/04/rust-in-android-platform.html 3. https://hacks.mozilla.org/2019/02/rewriting-a-browser-component-in-rust/ Vulnerabilidades de memoria son comunes
  36. error[E0382]: borrow of moved value: `s1` --> src/main.rs:5:28 | 2

    | let s1 = String::from("hello"); | -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait 3 | let s2 = s1; | -- value moved here 4 | 5 | println!("{}, world!", s1); | ^^ value borrowed here after move Rust - seguridad de memoria sin un recolector de basura El compilador checa la seguridad de memoria a través de su "borrow checker".
  37. Rust - concurrencia sin miedo El compilador checa la seguridad

    de concurrencia—incluyendo carreras de datos—a través de su "borrow checker". error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function --> src/main.rs:6:32 | 6 | let handle = thread::spawn(|| { | ^^ may outlive borrowed value `v` 7 | println!("Aquí hay un vector: {:?}", v); | - `v` is borrowed here
  38. Rust - rendimiento ~igual a C https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/gcc-rust.html

  39. Rust - otras ventajas • Sistema de tipo moderno, ~MyPy

    • Herramientas modernas • Buena documentación y mensajes de errores • Una comunidad inclusiva
  40. Rust - algunas desventajas • Más complejo que Python •

    Compilación lento • Todavía joven - no siempre hay bibliotecas maduras
  41. 3. ¿Qué sigue de esto?

  42. Recursos sobre Rust "The Rust Book" • Gratis • Es

    official • Hay una traducción, github.com/ManRR/rust-book-es doc.rust-lang.org/stable/book
  43. Recursos sobre Rust Clase de Microsoft Learn (gratis) docs.microsoft.com/es-es/learn/paths/rust-first-steps/

  44. PyO3 - biblioteca para extensiones nativas en Rust pyo3.rs Ejemplo:

    orjson
  45. Resumen 1. El rendimiento no siempre importa mucho. 2. Perfilen.

    Intenten optimizar, prueben PyPy/Numba, y tal vez antes la concurrencia de Python. 3. Las extensiones nativas en Rust ofrecen rendimiento seguro y accesible. Rust + Python están listos para la producción.
  46. Sigan la conversación con el equipo Pants Twitter: @pantsbuild @EArellanoAZ

    Slack: pantsbuild.org para el enlace Pycon 2021 Startup Row: