1 puntos por GN⁺ 2023-12-26 | 1 comentarios | Compartir por WhatsApp
  • YJIT mejora considerablemente el rendimiento y el uso de memoria frente a Ruby 3.2, y en Optcarrot es más de 3 veces más rápido que el intérprete
  • Code GC de YJIT queda desactivado por defecto, y --yjit-exec-mem-size funciona como un límite estricto que detiene la compilación de código nuevo
  • Se agrega RubyVM::YJIT.enable, lo que permite activar YJIT en tiempo de ejecución sin cambiar argumentos de línea de comandos ni variables de entorno
  • Se agrega el parser Prism como gem por defecto, y están disponibles las API Prism.parse(source), Prism.parse_comments(source), Prism.parse_success?(source)
  • Se puede experimentar con el compilador Prism usando ruby --parser=prism o RUBYOPT="--parser=prism", pero esa bandera es solo para depuración
  • El generador de parsers se reemplaza de Bison a Lrama, y el parser interno de Lrama se reemplaza por un parser LR generado por Racc para mejorar la mantenibilidad
  • El compilador JIT en pure Ruby RJIT reemplaza a MJIT, pero solo soporta la arquitectura x86-64 en Unix y está limitado a fines experimentales
  • Se introduce un scheduler de hilos M:N, donde N hilos nativos gestionan M hilos de Ruby, pero puede romper la compatibilidad con extensiones en C, por lo que está desactivado por defecto en el Ractor principal
  • El uso de it cuando se llama sin argumentos y se usa en un bloque sin parámetros normales queda obsoleto (deprecation), y en Ruby 3.4 pasará a ser una referencia al primer parámetro del bloque
  • Regexp::new ahora permite como máximo 2 argumentos en lugar de la llamada con 3 argumentos que quedó obsoleta en Ruby 3.2
  • ext/readline se retira, y en adelante se dependerá de reline, una implementación en pure Ruby compatible con la API de ext/readline
  • Si se necesita ext/readline, puede instalarse desde rubygems.org con gem install readline-ext, y ya no hace falta instalar bibliotecas como libreadline o libedit
  • RubyGems y Bundler muestran una advertencia si se hace require de algunos gems sin agregarlos al Gemfile o al gemspec; esos gems pasarán a ser bundled gems en futuras versiones de Ruby
  • Al usar el gem bootsnap, esa advertencia se suprime; debido a una limitación de esta versión, se recomienda ejecutar la aplicación al menos una vez con la variable de entorno DISABLE_BOOTSNAP=1
  • Se agrega prism 0.19.0 como gem por defecto, y racc 1.7.3 asciende de default gem a bundled gem

1 comentarios

 
GN⁺ 2023-12-26
Opiniones de Hacker News
  • Con Ruby 3.3, veo que Ruby volvió con fuerza. Un lenguaje centrado en la felicidad del desarrollador, pero del que se burlaban por ser lento, ahora ya no es lento
    YJIT es una tecnología sorprendente y, junto con las shapes de objetos y varias optimizaciones de garbage collection, Ruby se está volviendo bastante rápido. Grandes usuarios de Ruby como Shopify también activaron YJIT en producción con versiones preliminares de 3.3 y reportaron mejoras de rendimiento de dos dígitos porcentuales
    Personalmente, me entusiasman mucho Ruby y su futuro, y quiero trabajar con Ruby 3.3 para aplicarlo en sitios en operación de clientes
    https://railsatscale.com/2023-09-18-ruby-3-3-s-yjit-runs-sho...

    • No sé si una pequeña mejora de rendimiento hará que alguien elija Ruby de nuevo, pero se agradecen las mejoras incrementales. Preocuparse demasiado por el rendimiento casi siempre es optimización prematura, y Ruby hasta ahora ha sido lo suficientemente rápido para lo que le he pedido
      También lo usé como código de pegamento entre un almacén de registros DNS basado en Redis y PowerDNS, y todo el stack procesaba 500 millones de consultas al mes en 14 VPS pequeños sin que ni se notara en htop. No usé PowerDNS por rendimiento, sino porque normalmente no es buena idea implementar uno mismo cosas como cifrado, HTTP o DNS expuestas a Internet público
      La ironía de la web actual es que una webapp hecha con Ruby “lento” y renderizado del lado del servidor carga páginas más rápido que una base de datos de consistencia eventual realmente lenta y una webapp frontend inflada. En la web moderna veo con demasiada frecuencia spinners de carga incluso para hacer cosas triviales
    • El lenguaje Ruby en sí puede ser rápido, pero todo el ecosistema es dolorosamente lento. Si haces en Rails un servidor que devuelve 1 MB de JSON por solicitud después de consultas a la base de datos y llamadas a otros servicios, daba unas 100 solicitudes por segundo; reescribimos el mismo servicio en Go y manejaba 100 mil solicitudes por segundo
    • Me preguntaba si “mejora de rendimiento de dos dígitos” significaba 10% más rápido o 10 veces más rápido
      Al abrir el enlace vi que era una mejora del orden del 10%. No creo que cambie mucho la percepción de que Ruby es aproximadamente 50 a 200 veces más lento que lenguajes “rápidos” como Rust, Java, Go o C++
    • Un área en la que Ruby podría mejorar la experiencia de desarrollo es una mejor experiencia de depuración. Tal vez estoy demasiado acostumbrado a Chrome DevTools, pero la última vez que depuré código Ruby con mucha metaprogramación fue doloroso entender qué estaba pasando
    • Al oír “dos dígitos” esperaba 50% u 80%, pero al ver el enlace era 13–15%
  • Ruby 3.3 parece uno de los lanzamientos de Ruby más importantes y con más funciones de la última década. No pensé que fueran a lanzar un JIT listo para producción antes que Python, y también están Prism, Lrama e IRB
    Pero lo que no se cubre lo suficiente son Ractor, el scheduler de hilos M:N, Fiber y Async. En especial, me gustaría escuchar opiniones sobre si hay gente usando estas funciones en producción, particularmente en el contexto de Rails

    • Que Ruby lanzara un JIT listo para producción antes que Python era bastante predecible. Ruby no tenía una enorme comunidad de cómputo científico que dependiera de cada detalle de implementación del intérprete anfitrión
    • Me pregunto si Ruby 3.3 es un lanzamiento tan importante. No sé qué lo hace tan significativo
      Aun así, Ractor merece más atención. Es una función enorme
    • De verdad no entiendo por qué no existe una sola cola de trabajos que abarque tanto Ruby como Python. Sé que en algún momento la gente empezó a intercambiar información mediante microservicios basados en HTTP, pero al final una cola de trabajos simple con un formato de almacenamiento compartido por ambos lenguajes sería una mejor forma de conectar sistemas basados en Ruby/Rails con stacks de machine learning
      Probablemente dentro de las empresas haya miles de soluciones personalizadas privadas basadas en RabbitMQ o Redis
  • Cada Navidad, como reloj, Ruby Lang saca una nueva versión

  • Disponible en Heroku: https://devcenter.heroku.com/changelog-items/2772

  • Se ve bien. El nuevo IRB es un intérprete interactivo de Ruby con mejor autocompletado y depuración. Creo que la mayoría de los cambios de Ruby 3.3 se traducen en mejoras de herramientas para desarrolladores
    La API del lenguaje se mantiene en general consistente con versiones anteriores. Escribí una guía para actualizar a Ruby 3.3 con notas para varios gestores de versiones: https://mac.install.guide/ruby/update.html
    Tengo curiosidad, pero no sé qué gestor de versiones recomendar. Antes recomendaba asdf, frum y chruby

    • No he tenido problemas particulares con rbenv ni asdf. Uso ambos en local y en producción
  • Ahora se puede interrumpir la resolución de nombres como Socket.getaddrinfo, y dicen que cada vez que se necesita resolver un nombre crean un worker pthread y ejecutan getaddrinfo(3) dentro de él
    Me pregunto si otros runtimes de lenguajes hacen algo parecido. Crear un hilo parece demasiado pesado, pero quizá en la práctica no sea un gran problema. Según sus propios benchmarks, el overhead es pequeño, aunque no cero
    10000.times { Addrinfo.getaddrinfo("www.ruby-lang.org";, 80) } tardaba 2.3 s antes del parche y 3.0 s después; 100.times { URI.open("https://www.ruby-lang.org";).read } tardaba 3.36 s antes del parche y 3.40 s después

    • Me pregunto si eso es porque todas las operaciones de entrada/salida de la biblioteca estándar deben soportar async/fiber
      Me dio la impresión de que, en vez de reimplementar la misma operación con el “color” async como en Python, todo se movió a asincrónico por defecto, pero no sé si eso es correcto
    • Me parece que un fiber sería más ligero que crear un hilo nuevo
  • Si ya conoces Python y NodeJS, surge la duda de si vale la pena aprender Ruby. Ruby parece interesante, pero se siente difícil.

    • Ruby es básicamente parecido a Python, pero menos popular y más elegante. Es un lenguaje generalista sólido, especialmente bueno para tareas como scripts de shell o limpieza de datos.
      Si dominas Node y Python, debería ser bastante fácil de aprender. La desventaja es que, si ya conoces esos lenguajes, no te va a dar algo fundamentalmente nuevo. La ventaja es sobre todo estética: Ruby ofrece y fomenta formas realmente hermosas de expresar código, y es divertido experimentarlo.
    • Creo que Ruby es mucho mejor que Python o Node para tareas tipo script de shell y programación interactiva/exploratoria orientada a trabajos de sistema. Usarlo como un “mejor bash” o un “mejor Perl” ya vale la pena.
      Trabajo principalmente en bases de código TypeScript, pero cuando manejo datos de logs o texto semiestructurado, o cuando hago reescrituras basadas en expresiones regulares en varios archivos, suelo recurrir a Ruby. Ruby es muy divertido; probablemente sea el lenguaje más divertido entre los que he usado con regularidad, así que eso ya es una recompensa en sí misma.
    • Depende de qué quieras obtener. Lo más interesante es que Ruby, en muchos aspectos, es muy parecido a Python y al mismo tiempo bastante distinto.
      El ejemplo más grande es que a Ruby realmente le encantan los bloques. Están por todos lados en la biblioteca estándar, hay mucho azúcar sintáctico y existen innumerables DSL centrados en bloques. Las capacidades funcionales estándar también están en la biblioteca estándar desde hace mucho, así que se puede escribir en un estilo funcional de forma fluida y legible.
      Técnicamente, en Python también se puede hacer la mayoría de eso, pero escribirlo y leerlo se ve mucho más incómodo. Python tiene lambdas, pero da la impresión de que no le gustan demasiado más allá de usos triviales; en cambio, las funciones se usan como valores de primera clase en todas partes.
    • Si quieres obtener pistas para mejorar tu código mientras aprendes, conviene usar Rubocop. Es un excelente linter y maestro.
    • Hace poco más de 10 años, entre 2011 y 2013, usé un poco Ruby; más precisamente, el Ruby de Rails. En ese momento todavía quedaba el eco de RoR, que había aparecido en 2005 y se había vuelto muy popular entre 2007 y 2009.
      La gente más atenta a las tecnologías nuevas se estaba pasando a un desarrollo orientado a APIs basado en node.js y AngularJS, mucho más rápido que lo hecho con RoR. En 2013, la forma estándar de RoR seguía siendo monolítica, y al no adaptarse bien al nuevo paradigma de separación entre frontend y backend, se veía envejecida.
      La razón por la que hablo tanto del framework hecho con Ruby y no de Ruby en sí es que sospecho que, incluso hoy, el 95% del desarrollo en Ruby son aplicaciones Rails. Ruby se hizo famoso en gran parte gracias a Ruby on Rails, y si RoR está en declive, Ruby probablemente seguirá la misma trayectoria que Perl hasta quedar reducido a una pequeña comunidad entusiasta.
      Mirando solo el lenguaje, no se me ocurre una razón para elegir Ruby en lugar de Python o TypeScript. No parece que Ruby haga nada mejor que sus competidores ya mejor establecidos, ni como lenguaje ni como plataforma.
  • Prism es interesante. Me pregunto si hay herramientas de análisis de código Ruby que lo usen. Estaba buscando una forma de analizar el código de la empresa.

  • Dicen que la variable de entorno RUBY_MAX_CPU=n configura un máximo de N, es decir, el número máximo de hilos nativos, y que el valor predeterminado es 8.
    Me parece que el valor predeterminado debería ser la cantidad de núcleos lógicos, como en Tokio de Rust o en muchos runtimes M:N.

    • Es una optimización que se puede agregar después y tiene algunas sutilezas. Si no recuerdo mal, Go fue similar durante algunos años, y recuerdo configurar GO_MAX_PROCS en init() o main().
    • No parece correcto imponer un límite máximo fijo a la cantidad de CPU. ¿No bastaría con depender de la cantidad de núcleos disponibles?
  • En Ruby 4.0 me gustaría que se permitiera un import explícito, en lugar de la forma actual de traer gems a un espacio de nombres global e implícito.

    • Puede que te interese Im de Chris Salzberg. Ya se puede usar en Ruby 3.2: https://github.com/shioyama/im
      También hay una discusión sobre la función tentativa “namespace on read”: https://bugs.ruby-lang.org/issues/19744
    • Creo que el espacio de nombres global, sea implícito o explícito, está bien. En teoría resulta incómodo, pero en la práctica rara vez causa problemas gracias a las convenciones y normas sociales.
      El hecho de que el espacio de nombres global solo admita exactamente una versión de cada gem también fomenta una cultura sana que valora una ABI estable y los períodos de gracia para la obsolescencia. Comparado con algunos ecosistemas de lenguajes, es un entorno de ensueño.
    • Me parece que eso es más de Rails que de Ruby. Sé que en la mayoría de los proyectos esa diferencia prácticamente no existe, pero podría ser una decisión del equipo de Rails y no tener nada que ver con lo que hace el equipo de Ruby.
      O tal vez estoy diciendo algo completamente equivocado.
    • Ruby 4.0 difícilmente se verá antes de 2030.
      https://youtu.be/4MM5b2F9zrM?si=UT3aUbD6p_uBZehS&t=2347