No entraré en detalles porque es complejo. Y a continuación, está la estructura del item, que representa una entrada en el archivo JSON, ¿verdad? Su atributo score coincide con el campo del JSON, y ignoramos los atributos ID y name del JSON ya que no los necesitamos para calcular la puntuación más alta. Y luego, finalmente, tenemos la función de puntuación más alta en sí, que lee el contenido del archivo, deserializa un vector de estructuras de item del JSON, y finalmente itera sobre todos esos elementos en el vector y encuentra la puntuación máxima. No entraré en más detalles aquí. Solo quiero mencionar una cosa. Este código usa unwrap en varios lugares, lo cual es realmente malo, y solo lo estoy haciendo aquí para mantener el código simple. Así que ves que las funciones readToString y fromString y maxByKey todas devuelven resultados, ¿verdad? lo que significa que pueden fallar, y realmente deberíamos manejar tanto el caso de éxito como el de error, como mostré antes.
Llamar a unwrap en esos resultados simplemente significa fallar en tiempo de ejecución en caso de un error, lo cual es algo que nadie debería hacer en código de producción, así que me avergüenzo de mostrarte esto, pero como esto es un ejemplo y queremos mantener el código simple, lo estamos haciendo, pero solo recuerda, no hagas esto realmente, ¿verdad? De todos modos, teniendo este código Rust, podemos compilarlo, ¿verdad? No necesitamos preocuparnos por los detalles aquí. Una aplicación lo configura todo por ti, y una vez que está compilado, podemos usarlo fácilmente desde nuestro servidor express en JavaScript, ¿verdad? Así que aquí importamos en la línea dos la implementación de Rust, y luego en el endpoint de puntuación más alta, usamos la función de Rust para calcular la puntuación más alta en lugar de la implementación anterior en JavaScript, por lo que los cambios en el código JavaScript son bastante mínimos, y podemos usar la función de Rust completamente de manera transparente y mantener la arquitectura general tal como está, ¿verdad? El efecto de esto, sin embargo, es un servidor sustancialmente más rápido, ¿verdad? El tiempo de respuesta para el percentil 97.5 ahora es de aproximadamente 1.7 segundos, ¿verdad? casi el doble de rápido que antes, y en lugar de 49 solicitudes en total, ahora manejamos 122, ¿verdad?
más del doble. Mirando las cosas un poco más a fondo, todavía estamos viendo, todavía estamos maximizando la CPU, por supuesto, ¿verdad? ya que esta es una tarea dependiente de la CPU, y eso siempre será el caso, pero estamos usando la CPU de manera más eficiente, ¿verdad? Así que la estamos maximizando por un período de tiempo más corto, o por la misma cantidad de tiempo para el benchmark de 10 segundos, pero podemos manejar más solicitudes al mismo tiempo. También es importante notar que todavía estamos usando solo un núcleo aquí, ¿verdad? Dado que Rust es multihilo, podríamos cambiar el código relativamente fácil para aprovechar más núcleos, aumentando aún más la ganancia de rendimiento y, como, por un margen considerable. Y también estamos usando mucho menos memoria, ¿verdad? y el uso de memoria es estable ya que no hay recolección de basura, ¿verdad? Verás que es esencialmente una línea plana la mayor parte del tiempo. Así que aunque este es un ejemplo bastante poco realista, verás que obviamente hay una diferencia bastante sustancial en el rendimiento entre Node y Rust, y fue relativamente fácil desbloquear ese tipo de ganancia de rendimiento, ¿verdad? Y también hay riesgos mínimos. También hay un riesgo mínimo para los problemas con los que nos encontraríamos con C ya que Rust nos protege de ellos, como mostré antes. Así que echemos un breve vistazo a cómo funciona esto bajo el capó. En package.json, tenemos un nuevo script de compilación, ¿verdad? que nappy generó, que usamos antes para compilar el código. Así que eso compila el código Rust en el módulo nativo binario. En este caso, es jsnation-demo-darwin-arm64.node. También genera un archivo index.js a través del cual se puede usar el modelo nativo desde JavaScript, ¿verdad? Recuerda en el ejemplo anterior que mostré, importamos la función de Rust desde index.js, no desde ese archivo .node. Y el archivo index.js simplemente carga el binario correcto para la plataforma actual, ¿verdad? En mi Mac, la plataforma es ARM64, así que esto cargaría el archivo jsnation-demo-darwin-arm64.node que vimos antes. Así que si ahora te propones escribir módulos nativos en Rust, hay algunas cosas a considerar. Aunque los módulos nativos son excelentes para hacer cosas lentas más rápidas, obviamente no todo lo que es lento se beneficiará. Las tareas dependientes de I.O. como leer de una base de datos, por ejemplo, no se beneficiarán ya que tu código no es lo que es lento, sino los recursos externos, ¿verdad? Así que hacer tu código más rápido no tendrá mucho impacto. Así que analiza cuidadosamente antes de proponerte escribir módulos nativos. Los escenarios típicos para módulos nativos son cargas de trabajo dependientes de la CPU, obviamente como en el ejemplo que compartí. También hay un poco de sobrecarga al cambiar de contexto entre Rust y JavaScript, y en particular, mover estructuras de datos a través de las fronteras puede ser costoso, ¿verdad? Porque los datos tendrán que ser transcodificados de representaciones de Rust a representaciones de JavaScript y viceversa cada vez, y eso puede ser bastante costoso y resultar en que algunas cosas incluso sean más lentas que una implementación pura de JavaScript. Y por último, Rust también es un gran lenguaje para compilar a JavaScript. Para compilar a WebAssembly. Sus restricciones lo hacen un gran ajuste para WebAssembly y las herramientas de WebAssembly en Rust también son bastante sólidas, ¿verdad? Así que eso significa que el código Rust que escribes para usarlo en módulos nativos también, en teoría, podrá tal vez reutilizarlo en el navegador o en funciones de borde como WebAssembly. Y eso es algo que vale la pena tener en cuenta al comenzar a introducir Rust en tu base de código. Así que espero que todos se conviertan en fanáticos de Rust ahora, y tu primer módulo nativo en Rust podría ser solo el primer paso en un viaje más largo hacia más y más Rust, ¿verdad? Podrías escribir más y más módulos nativos en Rust, tal vez reutilizar el código en WebAssembly, y eventualmente desplegar tu primer microservicio en Rust, y tal vez eventualmente tu viaje con Rust te lleve a EuroRust, la conferencia líder de Rust en Europa, que casualmente organizamos.
Comments