window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = finestra if (w.LeadBooster) { console.warn('LeadBooster esiste già') } 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 }) }, } } })() FORKING E THREADING IN RUBY - The Codest
The Codest
  • Chi siamo
  • Servizi
    • Sviluppo di software
      • Sviluppo Frontend
      • Sviluppo backend
    • Staff Augmentation
      • Sviluppatori Frontend
      • Sviluppatori backend
      • Ingegneri dei dati
      • Ingegneri del cloud
      • Ingegneri QA
      • Altro
    • Consulenza
      • Audit e consulenza
  • Industrie
    • Fintech e banche
    • E-commerce
    • Adtech
    • Tecnologia della salute
    • Produzione
    • Logistica
    • Automotive
    • IOT
  • Valore per
    • CEO
    • CTO
    • Responsabile della consegna
  • Il nostro team
  • Case Studies
  • Sapere come
    • Blog
    • Incontri
    • Webinar
    • Risorse
Carriera Contattate
  • Chi siamo
  • Servizi
    • Sviluppo di software
      • Sviluppo Frontend
      • Sviluppo backend
    • Staff Augmentation
      • Sviluppatori Frontend
      • Sviluppatori backend
      • Ingegneri dei dati
      • Ingegneri del cloud
      • Ingegneri QA
      • Altro
    • Consulenza
      • Audit e consulenza
  • Valore per
    • CEO
    • CTO
    • Responsabile della consegna
  • Il nostro team
  • Case Studies
  • Sapere come
    • Blog
    • Incontri
    • Webinar
    • Risorse
Carriera Contattate
Freccia indietro TORNA INDIETRO
2016-10-06
Sviluppo di software

FORKING E THREADING IN RUBY

Marek Gierlach

Come probabilmente saprete, Ruby ha diverse implementazioni, come MRI, JRuby, Rubinius, Opal, RubyMotion e così via, e ognuna di esse può utilizzare un diverso modello di esecuzione del codice. Questo articolo si concentrerà sulle prime tre e metterà a confronto MRI

Come probabilmente sapete, Ruby ha diverse implementazioni, come MRI, JRuby, Rubinius, Opal, RubyMotion e così via, e ognuna di esse può utilizzare uno schema diverso di codice esecuzione. Questo articolo si concentrerà sui primi tre e metterà a confronto la risonanza magnetica (attualmente l'implementazione più diffusa) con JRuby e Rubinius, eseguendo alcuni script di esempio che dovrebbero valutare l'idoneità del forking e del threading in varie situazioni, come l'elaborazione di algoritmi ad alta intensità di CPU, la copia di file ecc.

Forcella

  • è un nuovo processo figlio (una copia di quello padre)
  • ha un nuovo identificatore di processo (PID)
  • ha una memoria separata*
  • comunica con altri tramite canali di comunicazione interprocesso (IPC) come code di messaggi, file, socket, ecc.
  • esiste anche quando il processo padre termina
  • è una chiamata POSIX, che funziona principalmente su piattaforme Unix.

Il filo

  • è "solo" un contesto di esecuzione, che lavora all'interno di un processo
  • condivide tutta la memoria con gli altri (per impostazione predefinita usa meno memoria di un fork)
  • comunica con gli altri tramite oggetti di memoria condivisa
  • muore con un processo
  • introduce i tipici problemi del multi-threading, come starvation, deadlock, ecc.

Esistono molti strumenti che utilizzano fork e thread e che vengono utilizzati quotidianamente, ad esempio Unicorn (fork) e Puma (thread) a livello di application server, Resque (fork) e Sidekiq (thread) a livello di lavori in background, ecc.

La tabella seguente presenta il supporto del forking e del threading nelle principali implementazioni di Ruby.

Implementazione di RubyForcaturaFilettatura
RISONANZA MAGNETICASìSì (limitato da GIL**)
JRuby–Sì
RubiniusSìSì

In questo argomento tornano come un boomerang altre due parole magiche: parallelismo e concorrenza. Innanzitutto, questi termini non possono essere usati in modo intercambiabile. In poche parole, possiamo parlare di parallelismo quando due o più task vengono elaborati esattamente nello stesso momento. La concomitanza ha luogo quando due o più task vengono elaborati in periodi di tempo sovrapposti (non necessariamente nello stesso momento). Certo, si tratta di una spiegazione ampia, ma sufficiente per aiutarvi a notare la differenza e a comprendere il resto di questo articolo.

Rapporto Frontiera per il 2020

La tabella seguente presenta il supporto per il parallelismo e la concorrenza.

Implementazione di RubyParallelismo (tramite fork)Parallelismo (tramite thread)Concorrenza
RISONANZA MAGNETICASìNoSì
JRuby–SìSì
RubiniusSìSì (dalla versione 2.X)Sì

Questa è la fine della teoria: vediamola in pratica!

  • Avere una memoria separata non significa necessariamente consumare la stessa quantità di memoria del processo padre. Esistono alcune tecniche di ottimizzazione della memoria. Una di queste è Copy on Write (CoW), che consente al processo genitore di condividere la memoria allocata con il processo figlio senza copiarla. Con CoW la memoria aggiuntiva è necessaria solo in caso di modifica della memoria condivisa da parte di un processo figlio. Nel contesto di Ruby, non tutte le implementazioni sono compatibili con il CoW, ad esempio MRI lo supporta pienamente dalla versione 2.X. Prima di questa versione, ogni fork consumava tanta memoria quanto un processo padre.
  • Uno dei maggiori vantaggi/svantaggi della risonanza magnetica (eliminare l'alternativa inappropriata) è l'uso del GIL (Global Interpreter Lock). In poche parole, questo meccanismo è responsabile della sincronizzazione dell'esecuzione dei thread, il che significa che solo un thread può essere eseguito alla volta. Ma aspettate... Significa che non ha senso usare i thread nella risonanza magnetica? La risposta arriva con la comprensione degli interni di GIL... o almeno dando un'occhiata agli esempi di codice in questo articolo.

Caso di test

Per presentare il funzionamento del forking e del threading nelle implementazioni di Ruby, ho creato una semplice classe chiamata Test e alcune altre che ereditano da essa. Ogni classe ha un compito diverso da elaborare. Per impostazione predefinita, ogni task viene eseguito quattro volte in un ciclo. Inoltre, ogni task viene eseguito contro tre tipi di esecuzione del codice: sequenziale, con fork e con thread. Inoltre, Benchmark.bmbm esegue il blocco di codice due volte: la prima volta per far funzionare l'ambiente di runtime, la seconda per effettuare le misurazioni. Tutti i risultati presentati in questo articolo sono stati ottenuti nella seconda esecuzione. Naturalmente, anche bmbm non garantisce un isolamento perfetto, ma le differenze tra più esecuzioni di codice sono insignificanti.

richiedere "benchmark"

classe Test
  IMPORTO = 4

  def Esegui
    Benchmark.bmbm do |b|
      b.report("sequenziale") { sequenziale }
      b.report("forking") { forking }
      b.report("threading") { threading }
    fine
  fine

  privato

  def sequenziale
    IMPORTO.volte { eseguire }
  end

  def biforcazione
    IMPORTO.volte do
      biforcazione do
        eseguire
      fine
    fine

    Processo.waitall
  rescue NotImplementedError => e
    # il metodo fork non è disponibile in JRuby
    mette e
  fine

  def threading
    threads = []

    IMPORTO.volte do
      threads << Thread.new do
        eseguire
      fine
    fine

    threads.map(&:join)
  fine

  def eseguire
    solleva "non implementato"
  fine
fine
Prova di carico

Esegue i calcoli in un ciclo per generare un grande carico della CPU.

classe LoadTest < Test
  def eseguire
    1000.times { 1000.times { 2**3**4 } }
  end
fine

Eseguiamolo...

LoadTest.new.run

...e controllare i risultati

RISONANZA MAGNETICAJRubyRubinius
sequenziale1.8629282.0890001.918873
biforcazione0.945018–1.178322
filettatura1.9139821.1070001.213315

Come si può vedere, i risultati delle esecuzioni sequenziali sono simili. Naturalmente c'è una piccola differenza tra le soluzioni, ma è causata dall'implementazione sottostante dei metodi scelti nei vari interpreti.

La biforcazione, in questo esempio, comporta un notevole guadagno di prestazioni (il codice viene eseguito quasi due volte più velocemente).

Il threading dà risultati simili a quelli del forking, ma solo per JRuby e Rubinius. L'esecuzione del campione con i thread su MRI richiede un po' più di tempo rispetto al metodo sequenziale. Le ragioni sono almeno due. In primo luogo, GIL forza l'esecuzione sequenziale dei thread, quindi in un mondo perfetto il tempo di esecuzione dovrebbe essere lo stesso dell'esecuzione sequenziale, ma si verifica anche una perdita di tempo per le operazioni di GIL (passaggio da un thread all'altro, ecc.). In secondo luogo, è necessario un certo tempo di overhead per la creazione dei thread.

Questo esempio non ci dà una risposta alla domanda sul senso dei fili d'uso nella risonanza magnetica. Vediamone un altro.

Test Snooze

Esegue un metodo di sospensione.

classe SnoozeTest < Test
  def eseguire
    dormire 1
  fine
fine

Ecco i risultati

RISONANZA MAGNETICAJRubyRubinius
sequenziale4.0046204.0060004.003186
biforcazione1.022066–1.028381
filettatura1.0015481.0040001.003642

Come si può vedere, ogni implementazione fornisce risultati simili non solo nelle esecuzioni sequenziali e di biforcazione, ma anche in quelle di threading. Allora, perché MRI ha lo stesso incremento di prestazioni di JRuby e Rubinius? La risposta è nell'implementazione di dormire.

Risonanza magnetica dormire è implementato con il metodo rb_thread_wait_for C, che utilizza un'altra funzione chiamata nativo_sleep. Diamo una rapida occhiata alla sua implementazione (il codice è stato semplificato, l'implementazione originale si può trovare qui):

vuoto statico
native_sleep(rb_thread_t *th, struct timeval *timeout_tv)
{
  ...

  GVL_UNLOCK_BEGIN();
  {
    // fa qualcosa qui
  }
  GVL_UNLOCK_END();

  thread_debug("native_sleep donen");
 }

Il motivo per cui questa funzione è importante è che, oltre a utilizzare il contesto stretto di Ruby, passa anche a quello di sistema per eseguire alcune operazioni. In situazioni come questa, il processo Ruby non ha nulla da fare... Un grande esempio di perdita di tempo? Non proprio, perché c'è un GIL che dice: "Non c'è niente da fare in questo thread? Passiamo a un altro e torniamo qui dopo un po'". Questo può essere fatto sbloccando e bloccando il GIL con GVL_UNLOCK_BEGIN() e GVL_UNLOCK_END() funzioni.

La situazione diventa chiara, ma dormire è raramente utile. Abbiamo bisogno di altri esempi di vita reale.

Test di download dei file

Esegue un processo che scarica e salva un file.

richiedere "net/http"

classe DownloadFileTest < Test
  def eseguire
    Net::HTTP.get("upload.wikimedia.org", "/wikipedia/commons/thumb/7/73/Ruby_logo.svg/2000px-Ruby_logo.svg.png")
  fine
fine

Non è necessario commentare i risultati seguenti. Sono abbastanza simili a quelli dell'esempio precedente.

1.003642JRubyRubinius
sequenziale0.3279800.3340000.329353
biforcazione0.104766–0.121054
filettatura0.0857890.0940000.088490

Un altro buon esempio può essere il processo di copia dei file o qualsiasi altra operazione di I/O.

Conclusioni

  • Rubinius supporta pienamente sia la biforcazione che il threading (dalla versione 2.X, quando GIL è stato rimosso). Il codice può essere concorrente ed eseguito in parallelo.
  • JRuby fa un buon lavoro con i thread, ma non supporta affatto il fork. Il parallelismo e la concorrenza possono essere ottenuti con i thread.
  • RISONANZA MAGNETICA supporta il forking, ma il threading è limitato dalla presenza di GIL. La concorrenza potrebbe essere ottenuta con i thread, ma solo quando il codice in esecuzione esce dal contesto dell'interprete Ruby (ad esempio operazioni di IO, funzioni del kernel). Non c'è modo di ottenere il parallelismo.

Articoli correlati

Sviluppo di software

Costruire applicazioni web a prova di futuro: le intuizioni del team di esperti di The Codest

Scoprite come The Codest eccelle nella creazione di applicazioni web scalabili e interattive con tecnologie all'avanguardia, offrendo esperienze utente senza soluzione di continuità su tutte le piattaforme. Scoprite come la nostra esperienza favorisce la trasformazione digitale e il business...

IL CANCRO
Sviluppo di software

Le 10 principali aziende di sviluppo software con sede in Lettonia

Scoprite le migliori aziende di sviluppo software della Lettonia e le loro soluzioni innovative nel nostro ultimo articolo. Scoprite come questi leader tecnologici possono aiutarvi a migliorare la vostra attività.

thecodest
Soluzioni per aziende e scaleup

Essenziali di sviluppo software Java: Guida all'outsourcing di successo

Esplorate questa guida essenziale sullo sviluppo di software Java con successo outsourcing per migliorare l'efficienza, accedere alle competenze e guidare il successo del progetto con The Codest.

thecodest
Sviluppo di software

La guida definitiva all'outsourcing in Polonia

L'aumento di outsourcing in Polonia è guidato dai progressi economici, educativi e tecnologici, che favoriscono la crescita dell'IT e un clima favorevole alle imprese.

IlCodesto
Soluzioni per aziende e scaleup

Guida completa agli strumenti e alle tecniche di audit IT

Gli audit IT garantiscono sistemi sicuri, efficienti e conformi. Per saperne di più sulla loro importanza, leggete l'articolo completo.

The Codest
Jakub Jakubowicz CTO e cofondatore

Iscrivetevi alla nostra knowledge base e rimanete aggiornati sulle competenze del settore IT.

    Chi siamo

    The Codest - Società internazionale di sviluppo software con centri tecnologici in Polonia.

    Regno Unito - Sede centrale

    • Ufficio 303B, 182-184 High Street North E6 2JA
      Londra, Inghilterra

    Polonia - Poli tecnologici locali

    • Parco uffici Fabryczna, Aleja
      Pokoju 18, 31-564 Cracovia
    • Ambasciata del cervello, Konstruktorska
      11, 02-673 Varsavia, Polonia

      The Codest

    • Casa
    • Chi siamo
    • Servizi
    • Case Studies
    • Sapere come
    • Carriera
    • Dizionario

      Servizi

    • Consulenza
    • Sviluppo di software
    • Sviluppo backend
    • Sviluppo Frontend
    • Staff Augmentation
    • Sviluppatori backend
    • Ingegneri del cloud
    • Ingegneri dei dati
    • Altro
    • Ingegneri QA

      Risorse

    • Fatti e miti sulla collaborazione con un partner esterno per lo sviluppo di software
    • Dagli Stati Uniti all'Europa: Perché le startup americane decidono di trasferirsi in Europa
    • Confronto tra gli hub di sviluppo Tech Offshore: Tech Offshore Europa (Polonia), ASEAN (Filippine), Eurasia (Turchia)
    • Quali sono le principali sfide di CTO e CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Condizioni di utilizzo del sito web

    Copyright © 2025 di The Codest. Tutti i diritti riservati.

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