HeadlinesBriefing favicon HeadlinesBriefing.com

Principios para aplicaciones Tokio rápidas

Hacker News •
×

Voy de regreso de Rust Conf. En el Unconf, tuvimos una discusión productiva sobre la depuración y el benchmarking de aplicaciones asíncronas. Se compartieron muchas ideas interesantes. Intento enumerar algunas de ellas aquí, junto con algunas de mis propias experiencias. Este es el primer borrador de lo que espero pueda convertirse en un documento vivo de mejores prácticas. No dudes en abrir un issue o enviar un PR. Espero también añadir una aplicación de ejemplo en los próximos días que demuestre estos problemas junto con cómo se ve el trace de dial9.— Russell

Hay pocas reglas estrictas para escribir código que rinda bien en los runtimes de Tokio; la respuesta a tantas preguntas es "depende". El rendimiento de una carga de trabajo depende de qué más se esté ejecutando en el runtime en ese momento. ¡Por eso tantos problemas solo aparecen en producción! Escribir aplicaciones asíncronas que rindan bien es un equilibrio entre equidad y batching, contención y aislamiento. Este artículo expone algunos principios generales y cubre excepciones donde puedo. Asume familiaridad básica con el runtime de work-stealing de Tokio; se incluye un resumen de alto nivel en el apéndice.

Principios generales

Primero, determina si tienes un problema. Si empiezas a buscar señales de alerta en una aplicación Tokio, las encontrarás. Casi todas las aplicaciones reales que he visto tienen polls mucho más largos que los 10-100 microsegundos que Alice Ryhl recomienda en su excelente artículo What is Blocking?. Estos problemas pueden o no afectar las métricas o el comportamiento de la aplicación que realmente te importan. Es importante trabajar hacia atrás desde una métrica real que intentas mejorar. Por ejemplo, una aplicación puede tener polls largos que son completamente benignos; "arreglarlos" no impactará de forma medible las métricas orientadas al usuario. En la gran mayoría de los problemas que he encontrado, el problema estaba en el propio código de la aplicación, a menudo en la interacción entre múltiples componentes de un sistema distribuido (y no realmente en Tokio). dial9 ha dado mucha visibilidad a Tokio; al menos con la misma frecuencia con que encuentra un problema de Tokio, en realidad demuestra claramente la ausencia de uno. Por supuesto, a veces sí es un problema de Tokio. En términos de métricas de Tokio, la más útil es el histograma de latencia de planificación (schedule latency) añadido recientemente. La latencia de planificación es la cantidad de tiempo entre que tu tarea está lista para ejecutarse y Tokio realmente hace poll del future. Aunque esto no te dirá cuál es la causa, la latencia de planificación es el síntoma más común de interacciones deficientes entre Tokio y tu código.

Divide para latencia, agrupa para rendimiento. Cede (yield) con más frecuencia para optimizar la latencia. La baja latencia en muchas solicitudes requiere equidad entre conexiones. Considera Redis (o cualquier aplicación que soporte pipelining de solicitudes). Una implementación ingenua leerá datos directamente de la conexión mientras haya más datos disponibles. Cuando las solicitudes están en pipeline, toda la solicitud en pipeline terminará en un búfer en memoria. Cuando lees frames de él, cada uno será Poll::Ready sin volver a la red. Esto crea tanto polls largos como inequidad entre clientes. El impacto en el rendimiento suele ser menor: se procesa el mismo número de solicitudes. La latencia, sin embargo, cambia drásticamente porque todo un pipeline puede esperar detrás de otro. Ceder explícitamente después de cada solicitud puede reducir la latencia en aproximadamente 10× en este ejemplo. Puedes hacerlo aún mejor cediendo solo después de varias lecturas consecutivas inmediatamente listas.

Entidades clave: Empresas: Tokio, Redis | Personas: Russell, Alice Ryhl | Ubicaciones: Rust Conf