4 puntos por GN⁺ 2024-03-26 | 1 comentarios | Compartir por WhatsApp
  • En la comunidad de Rust aparece con frecuencia la pregunta: si los hilos pueden hacer todo lo que async/await puede hacer y además son más simples, ¿por qué elegir async/await?
  • Rust es un lenguaje de bajo nivel y no oculta la complejidad de las corrutinas. Esto contrasta con lenguajes como Go, donde todo se vuelve asíncrono por defecto sin que el programador tenga que pensar en ello.
  • Los programadores inteligentes intentan evitar la complejidad, así que ¿por qué es necesario async/await?

Conocer el contexto

  • Rust es un lenguaje de bajo nivel. El código suele ser lineal: cuando una tarea termina, se ejecuta otra.
  • Cuando se necesita ejecutar muchas tareas al mismo tiempo, como en un servidor web, el código lineal genera problemas.
  • La web en sus inicios intentó resolver este problema introduciendo hilos.
  • Con hilos es posible atender a varios clientes al mismo tiempo, pero los programadores querían llevar la concurrencia del espacio del sistema operativo al espacio de usuario.

El problema de los timeouts

  • Una de las mayores fortalezas de Rust es su composabilidad.
  • async/await permite aplicar esa composabilidad a funciones I/O-bound.
  • Por ejemplo, si se quiere agregar un timeout a una función que atiende a un cliente, puede implementarse usando dos combinadores.

Hilos temáticos

  • En un ejemplo basado en hilos, implementar un timeout no es tan sencillo.
  • TcpStream tiene las funciones set_read_timeout y set_write_timeout, pero su uso es limitado.
  • Se presenta una forma de programar timeouts usando combinadores de Rust, pero está limitada a TcpStream y requiere llamadas adicionales al sistema.

Casos de éxito de async

  • El ecosistema HTTP adoptó async/await como su principal mecanismo de runtime.
  • tower es un ejemplo de la potencia de async/await, ya que ofrece timeouts, rate limiting, balanceo de carga y más.
  • macroquad es un motor de juegos en Rust que ejecuta el motor usando async/await.

Mejorar la imagen de Async

  • Como las ventajas de async no son ampliamente conocidas, algunas personas pueden malinterpretarlo.
  • La comunidad de Rust tiende a sobrevalorar las ventajas de rendimiento de async Rust y a minimizar sus beneficios realmente significativos.
  • async/await debe verse como un modelo de programación potente que permite expresar de forma concisa patrones que en Rust síncrono no pueden representarse sin decenas de hilos y canales.

Opinión de GN⁺

  • async/await aumenta la complejidad del código al manejar concurrencia, pero al mismo tiempo ofrece la capacidad de atender eficientemente a muchos clientes simultáneamente.
  • Este artículo subraya que async/await tiene fortalezas en el modelo de programación que van más allá de las simples ventajas de rendimiento.
  • En Rust, async/await aporta composabilidad para diversas tareas de I/O, lo que resulta especialmente útil en áreas como servicios de red o servidores web.
  • Desde una mirada crítica, la complejidad de async/await puede convertirse en una barrera de entrada para desarrolladores principiantes, por lo que se requiere un esfuerzo educativo para superarla.
  • Otros proyectos que ofrecen funcionalidades similares incluyen la implementación de async/await en Node.js y la biblioteca asyncio de Python, que también proporcionan un paradigma parecido.
  • Al adoptar async/await, es importante considerar la complejidad y la mantenibilidad del código; aun así, cuando se necesita atender a muchos clientes al mismo tiempo, este modelo ofrece grandes ventajas.

1 comentarios

 
GN⁺ 2024-03-26
Comentarios en Hacker News
  • Async/await y un solo hilo

    • El async/await en un solo hilo, como en el modelo de JavaScript, es simple y bien entendido.
    • Al usar hilos, varias CPU pueden procesar el problema, y Rust ayuda con la gestión de locks.
    • Se pueden tener hilos con distintas prioridades, y eso es necesario cuando hay límites de cómputo.
    • El async/await multihilo es complejo. En secciones con límites de cómputo, el modelo puede colapsar.
    • El cómputo multihilo en Rust no funciona bien. Entre los problemas están:
      • Colapso por contención de futex: puede ser un problema en algunos asignadores de almacenamiento.
      • Inanición por mutex no justos: el Mutex estándar y los canales crossbeam-channel no son justos.
  • Async/await vs. hilos

    • La crítica no trata sobre la complejidad, sino sobre que el ecosistema se divide según la elección y una opción termina siendo inferior.
    • El ecosistema de Rust decidió que, para hacer trabajo de I/O, hay que usar async/await en todo.
    • Si Rust hubiera convertido las alternativas a async/await en abstracciones más componibles, la inconformidad habría desaparecido.
  • Problemas del artículo

    • Solo se presentó un ejemplo de servidor web, y está mal resuelto respecto a los hilos.
    • Los programadores quieren hilos conceptuales y semánticos, no hilos del sistema operativo.
    • Los hilos del sistema operativo son costosos, y queremos hilos baratos.
    • Hay problemas con la implementación de timeouts en el ejemplo del servidor web.
  • Puntos que no se trataron

    • Async/await se ejecuta en un solo hilo, así que no necesita locks ni sincronización.
    • La propagación de errores en async/await no es clara.
    • También debería mencionarse el backpressure en I/O de red.
  • Puntos importantes sobre la cancelación

    • Cualquier trabajo futuro puede cancelarse fácilmente.
    • La cancelación en hilos es compleja, y forzar la detención de un hilo no es confiable.
    • En el modelo async de Rust, se puede agregar externamente un timeout a todos los futures.
  • Una campaña de marketing alrededor de async/await

    • Async/await fue un error técnico y ha provocado un gran costo para la comunidad.
    • Rust sigue siendo el mejor lenguaje, pero preocupa que este debate continúe para siempre.
  • Async/await vs. fibras

    • Rust antes tenía hilos verdes y fueron eliminados intencionalmente.
    • La capacidad de descartar futures en cualquier momento conlleva un costo grande.
    • Es extraño elogiar la composabilidad de async/await.
  • Principales ventajas de async/await en Rust

    • Puede funcionar incluso en entornos sin hilos ni memoria dinámica.
    • Permite escribir código de forma concisa usando concurrencia.
  • Malentendidos sobre async/await

    • Hay personas que no entienden por qué se necesita un mecanismo de concurrencia en un solo hilo.
    • Async/await es útil para programación de UI, comunicación con la GPU y comunicación entre runtimes.
  • Razones para elegir async/await en lugar de hilos

    • Async/await puede reducir el uso de memoria del estado de clientes/solicitudes/tareas.
    • La compresión de estado es importante para el rendimiento en la actualidad, cuando la memoria es lenta.
    • Async/await y CPS son eficaces para reducir el uso de memoria por cliente.