window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', versión: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster ya existe') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() BIFURCACIÓN E HILOS EN RUBY - The Codest
The Codest
  • Quiénes somos
  • Servicios
    • Desarrollo de software
      • Desarrollo Frontend
      • Desarrollo backend
    • Staff Augmentation
      • Desarrolladores frontales
      • Desarrolladores de backend
      • Ingenieros de datos
      • Ingenieros de la nube
      • Ingenieros de control de calidad
      • Otros
    • Asesoramiento
      • Auditoría y consultoría
  • Industrias
    • Fintech y Banca
    • E-commerce
    • Adtech
    • Tecnología sanitaria
    • Fabricación
    • Logística
    • Automoción
    • IOT
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • Nuestro equipo
  • Case Studies
  • Saber cómo
    • Blog
    • Meetups
    • Seminarios en línea
    • Recursos
Carreras profesionales Póngase en contacto
  • Quiénes somos
  • Servicios
    • Desarrollo de software
      • Desarrollo Frontend
      • Desarrollo backend
    • Staff Augmentation
      • Desarrolladores frontales
      • Desarrolladores de backend
      • Ingenieros de datos
      • Ingenieros de la nube
      • Ingenieros de control de calidad
      • Otros
    • Asesoramiento
      • Auditoría y consultoría
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • Nuestro equipo
  • Case Studies
  • Saber cómo
    • Blog
    • Meetups
    • Seminarios en línea
    • Recursos
Carreras profesionales Póngase en contacto
Flecha atrás VOLVER
2016-10-06
Desarrollo de software

BIFURCACIÓN E HILOS EN RUBY

Marek Gierlach

Como probablemente sepas, Ruby tiene unas cuantas implementaciones, como MRI, JRuby, Rubinius, Opal, RubyMotion, etc., y cada una de ellas puede utilizar un patrón diferente de ejecución de código. Este artículo se centrará en las tres primeras y comparará MRI

Como probablemente sepas, Ruby tiene unas cuantas implementaciones, como MRI, JRuby, Rubinius, Opal, RubyMotion, etc., y cada una de ellas puede utilizar un patrón diferente de código ejecución. Este artículo se centrará en los tres primeros y comparará MRI (actualmente la implementación más popular) con JRuby y Rubinius mediante la ejecución de algunos scripts de ejemplo que se supone que evalúan la idoneidad de la bifurcación y la creación de hilos en diversas situaciones, como el procesamiento de algoritmos intensivos de CPU, la copia de archivos, etc. Antes de empezar a "aprender haciendo", es necesario revisar algunos términos básicos.

Horquilla

  • es un nuevo proceso hijo (una copia del proceso padre)
  • tiene un nuevo identificador de proceso (PID)
  • tiene memoria independiente*
  • se comunica con otros a través de canales de comunicación entre procesos (IPC) como colas de mensajes, archivos, sockets, etc.
  • existe incluso cuando el proceso padre finaliza
  • es una llamada POSIX - funciona principalmente en plataformas Unix

Hilo

  • es "sólo" un contexto de ejecución, que trabaja dentro de un proceso
  • comparte toda la memoria con los demás (por defecto utiliza menos memoria que un fork)
  • se comunica con otros mediante objetos de memoria compartida
  • muere con un proceso
  • introduce los típicos problemas de multihilo, como la inanición, los bloqueos, etc.

Existen multitud de herramientas que utilizan bifurcaciones (forks) e hilos (threads), que se utilizan a diario, por ejemplo, Unicorn (bifurcaciones) y Puma (hilos) a nivel de servidores de aplicaciones, Resque (bifurcaciones) y Sidekiq (hilos) a nivel de trabajos en segundo plano, etc.

La siguiente tabla presenta el soporte para bifurcaciones e hilos en las principales implementaciones de Ruby.

Aplicación de RubyBifurcaciónEnhebrado
IRMSíSí (limitado por el GIL**)
JRuby–Sí
RubinioSíSí

Otras dos palabras mágicas vuelven como un boomerang en este tema - paralelismo y concurrencia - tenemos que explicarlas un poco. En primer lugar, estos términos no pueden utilizarse indistintamente. En pocas palabras, podemos hablar de paralelismo cuando dos o más tareas se procesan exactamente al mismo tiempo. La concurrencia tiene lugar cuando dos o más tareas están siendo procesadas en periodos de tiempo solapados (no necesariamente al mismo tiempo). Sí, es una explicación amplia, pero lo suficientemente buena como para ayudarte a notar la diferencia y entender el resto de este artículo.

Informe Fronented para 2020

La siguiente tabla presenta el soporte para paralelismo y concurrencia.

Aplicación de RubyParalelismo (mediante bifurcaciones)Paralelismo (mediante hilos)Concurrencia
IRMSíNoSí
JRuby–SíSí
RubinioSíSí (desde la versión 2.X)Sí

Se acabó la teoría: ¡vamos a verlo en la práctica!

  • Tener memoria separada no implica necesariamente consumir la misma cantidad que el proceso padre. Existen algunas técnicas de optimización de memoria. Una de ellas es Copy on Write (CoW), que permite al proceso padre compartir la memoria asignada con el proceso hijo sin copiarla. Con CoW se necesita memoria adicional sólo en el caso de que un proceso hijo modifique la memoria compartida. En el contexto Ruby, no todas las implementaciones son amigables con CoW, por ejemplo MRI lo soporta completamente desde la versión 2.X. Antes de esta versión cada fork consumía tanta memoria como un proceso padre.
  • Una de las mayores ventajas/desventajas de MRI (tachar la alternativa inapropiada) es el uso de GIL (Global Interpreter Lock). En pocas palabras, este mecanismo se encarga de sincronizar la ejecución de los hilos, lo que significa que sólo se puede ejecutar un hilo a la vez. Pero espera... ¿Significa esto que no tiene sentido utilizar hilos en la RM? La respuesta viene con la comprensión de las interioridades de GIL... o al menos echando un vistazo a los ejemplos de código de este artículo.

Caso de prueba

Para presentar cómo funcionan la bifurcación y el roscado en las implementaciones de Ruby, he creado una clase sencilla llamada Prueba y algunas otras que heredan de ella. Cada clase tiene una tarea diferente que procesar. Por defecto, cada tarea se ejecuta cuatro veces en un bucle. Además, cada tarea se ejecuta contra tres tipos de ejecución de código: secuencial, con bifurcaciones y con hilos. Además, Comparativa.bmbm ejecuta el bloque de código dos veces: la primera para poner en marcha el entorno de ejecución y la segunda para medir. Todos los resultados presentados en este artículo se obtuvieron en la segunda ejecución. Por supuesto, incluso bmbm no garantiza un aislamiento perfecto, pero las diferencias entre varias ejecuciones de código son insignificantes.

exigir "benchmark"

clase Prueba
  IMPORTE = 4

  def ejecutar
    Benchmark.bmbm do |b|
      b.report("sequential") { secuencial }
      b.report("forking") { bifurcación }
      b.report("threading") { threading }
    end
  end

  privado

  def secuencial
    AMOUNT.times { perform }
  end

  def bifurcación
    AMOUNT.times do
      bifurcar do
        perform
      end
    end

    Proceso.waitall
  rescue NotImplementedError => e
    # el método fork no está disponible en JRuby
    puts e
  end

  def threading
    hilos = []

    AMOUNT.times do
      hilos << Thread.new do
        realizar
      end
    end

    threads.map(&:join)
  end

  def realizar
    raise "no implementado"
  end
end
Prueba de carga

Ejecuta cálculos en bucle para generar una gran carga de CPU.

class PruebaDeCarga < Prueba
  def realizar
    1000.veces { 1000.veces { 2**3**4 } }
  end
end

Vamos a ejecutarlo...

LoadTest.new.run

...y comprueba los resultados

IRMJRubyRubinio
secuencial1.8629282.0890001.918873
bifurcación0.945018–1.178322
roscado1.9139821.1070001.213315

Como puede ver, los resultados de las ejecuciones secuenciales son similares. Por supuesto, hay una pequeña diferencia entre las soluciones, pero está causada por la implementación subyacente de los métodos elegidos en varios intérpretes.

La bifurcación, en este ejemplo, tiene una ganancia de rendimiento significativa (el código se ejecuta casi dos veces más rápido).

La ejecución con hilos da resultados similares a la bifurcación, pero sólo para JRuby y Rubinius. Ejecutar la muestra con hilos en MRI consume un poco más de tiempo que el método secuencial. Hay al menos dos razones. En primer lugar, GIL fuerza la ejecución secuencial de hilos, por lo que en un mundo perfecto el tiempo de ejecución debería ser el mismo que para la ejecución secuencial, pero también se produce una pérdida de tiempo para las operaciones GIL (cambio entre hilos, etc.). En segundo lugar, también se necesita cierto tiempo de sobrecarga para crear hilos.

Este ejemplo no nos da una respuesta a la pregunta sobre el sentido de los hilos de uso en la RM. Veamos otro.

Prueba Snooze

Ejecuta un método de suspensión.

clase SnoozeTest < Test
  def realizar
    dormir 1
  end
end

Estos son los resultados

IRMJRubyRubinio
secuencial4.0046204.0060004.003186
bifurcación1.022066–1.028381
roscado1.0015481.0040001.003642

Como puedes ver, cada implementación da resultados similares no sólo en las ejecuciones secuenciales y de bifurcación, sino también en las de hilos. Entonces, ¿por qué MRI tiene la misma ganancia de rendimiento que JRuby y Rubinius? La respuesta está en la implementación de dormir.

IRM dormir se aplica con rb_thread_wait_for C, que utiliza otra llamada sueño_nativo. Echemos un vistazo a su implementación (el código se ha simplificado, la implementación original se puede encontrar en aquí):

static void
native_sleep(rb_thread_t *th, struct timeval *timeout_tv)
{
  ...

  GVL_UNLOCK_BEGIN();
  {
    // haz algunas cosas aquí
  }
  GVL_UNLOCK_END();

  thread_debug("native_sleep donen");
 }

La razón por la que esta función es importante es que, además de utilizar el contexto estricto de Ruby, también cambia al del sistema para realizar allí algunas operaciones. En situaciones como esta, el proceso Ruby no tiene nada que hacer... ¿Gran ejemplo de pérdida de tiempo? En realidad no, porque hay un GIL que dice: "¿No hay nada que hacer en este hilo? Cambiemos a otro y volvamos aquí después de un rato". Esto podría hacerse desbloqueando y bloqueando el GIL con GVL_UNLOCK_BEGIN() y GVL_UNLOCK_END() funciones.

La situación se aclara, pero dormir rara vez es útil. Necesitamos más ejemplos de la vida real.

Prueba de descarga de archivos

Ejecuta un proceso que descarga y guarda un archivo.

require "net/http"

class DownloadFileTest < Prueba
  def realizar
    Net::HTTP.get("upload.wikimedia.org", "/wikipedia/commons/thumb/7/73/Ruby_logo.svg/2000px-Ruby_logo.svg.png")
  end
fin

No es necesario comentar los siguientes resultados. Son bastante similares a los del ejemplo anterior.

1.003642JRubyRubinio
secuencial0.3279800.3340000.329353
bifurcación0.104766–0.121054
roscado0.0857890.0940000.088490

Otro buen ejemplo podría ser el proceso de copia de archivos o cualquier otra operación de E/S.

Conclusiones

  • Rubinio soporta completamente tanto la bifurcación como los hilos (desde la versión 2.X, cuando se eliminó GIL). Tu código podría ser concurrente y ejecutarse en paralelo.
  • JRuby hace un buen trabajo con hilos, pero no soporta la bifurcación en absoluto. El paralelismo y la concurrencia podrían lograrse con hilos.
  • IRM soporta la bifurcación, pero la ejecución de hilos está limitada por la presencia de GIL. La concurrencia podría lograrse con hilos, pero sólo cuando el código en ejecución sale del contexto del intérprete Ruby (por ejemplo, operaciones IO, funciones del núcleo). No hay forma de conseguir paralelismo.

Artículos relacionados

Desarrollo de software

Crear aplicaciones web preparadas para el futuro: ideas del equipo de expertos de The Codest

Descubra cómo The Codest destaca en la creación de aplicaciones web escalables e interactivas con tecnologías de vanguardia, ofreciendo experiencias de usuario fluidas en todas las plataformas. Descubra cómo nuestra experiencia impulsa la transformación...

EL MEJOR
Desarrollo de software

Las 10 mejores empresas de desarrollo de software de Letonia

Conozca las principales empresas de desarrollo de software de Letonia y sus innovadoras soluciones en nuestro último artículo. Descubra cómo estos líderes tecnológicos pueden ayudarle a mejorar su negocio.

thecodest
Soluciones para empresas y escalas

Fundamentos del desarrollo de software Java: Guía para externalizar con éxito

Explore esta guía esencial sobre el desarrollo de software Java outsourcing con éxito para mejorar la eficiencia, acceder a la experiencia e impulsar el éxito de los proyectos con The Codest.

thecodest
Desarrollo de software

La guía definitiva para subcontratar en Polonia

El auge de las outsourcing en Polonia está impulsado por los avances económicos, educativos y tecnológicos, que fomentan el crecimiento de las TI y un clima favorable a las empresas.

TheCodest
Soluciones para empresas y escalas

Guía completa de herramientas y técnicas de auditoría informática

Las auditorías informáticas garantizan sistemas seguros, eficientes y conformes. Obtenga más información sobre su importancia leyendo el artículo completo.

The Codest
Jakub Jakubowicz CTO y Cofundador

Suscríbase a nuestra base de conocimientos y manténgase al día de la experiencia del sector informático.

    Quiénes somos

    The Codest - Empresa internacional de desarrollo de software con centros tecnológicos en Polonia.

    Reino Unido - Sede central

    • Oficina 303B, 182-184 High Street North E6 2JA
      Londres, Inglaterra

    Polonia - Centros tecnológicos locales

    • Parque de oficinas Fabryczna, Aleja
      Pokoju 18, 31-564 Cracovia
    • Embajada del Cerebro, Konstruktorska
      11, 02-673 Varsovia, Polonia

      The Codest

    • Inicio
    • Quiénes somos
    • Servicios
    • Case Studies
    • Saber cómo
    • Carreras profesionales
    • Diccionario

      Servicios

    • Asesoramiento
    • Desarrollo de software
    • Desarrollo backend
    • Desarrollo Frontend
    • Staff Augmentation
    • Desarrolladores de backend
    • Ingenieros de la nube
    • Ingenieros de datos
    • Otros
    • Ingenieros de control de calidad

      Recursos

    • Hechos y mitos sobre la cooperación con un socio externo de desarrollo de software
    • De EE.UU. a Europa: ¿Por qué las startups estadounidenses deciden trasladarse a Europa?
    • Comparación de los polos de desarrollo de Tech Offshore: Tech Offshore Europa (Polonia), ASEAN (Filipinas), Eurasia (Turquía)
    • ¿Cuáles son los principales retos de los CTO y los CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Condiciones de uso del sitio web

    Copyright © 2025 por The Codest. Todos los derechos reservados.

    es_ESSpanish
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean nl_NLDutch etEstonian elGreek es_ESSpanish