window.pipedriveLeadboosterConfig = { basis: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', versie: 2, } ;(functie () { var w = venster als (w.LeadBooster) { console.warn('LeadBooster bestaat al') } anders { w.LeadBooster = { q: [], on: functie (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: functie (n) { this.q.push({ t: 't', n: n }) }, } } })() FORKING EN THREADING IN ROBIJN - The Codest
The Codest
  • Over ons
  • Diensten
    • Software Ontwikkeling
      • Frontend ontwikkeling
      • Backend ontwikkeling
    • Staff Augmentation
      • Frontend ontwikkelaars
      • Backend ontwikkelaars
      • Gegevensingenieurs
      • Cloud Ingenieurs
      • QA ingenieurs
      • Andere
    • Het advies
      • Audit & Consulting
  • Industrie
    • Fintech & Bankieren
    • E-commerce
    • Adtech
    • Gezondheidstechnologie
    • Productie
    • Logistiek
    • Automotive
    • IOT
  • Waarde voor
    • CEO
    • CTO
    • Leveringsmanager
  • Ons team
  • Case Studies
  • Weten hoe
    • Blog
    • Ontmoetingen
    • Webinars
    • Bronnen
Carrière Neem contact op
  • Over ons
  • Diensten
    • Software Ontwikkeling
      • Frontend ontwikkeling
      • Backend ontwikkeling
    • Staff Augmentation
      • Frontend ontwikkelaars
      • Backend ontwikkelaars
      • Gegevensingenieurs
      • Cloud Ingenieurs
      • QA ingenieurs
      • Andere
    • Het advies
      • Audit & Consulting
  • Waarde voor
    • CEO
    • CTO
    • Leveringsmanager
  • Ons team
  • Case Studies
  • Weten hoe
    • Blog
    • Ontmoetingen
    • Webinars
    • Bronnen
Carrière Neem contact op
Pijl terug KEREN TERUG
2016-10-06
Software Ontwikkeling

FORKING EN THREADING IN ROBIJN

Marek Gierlach

Zoals je waarschijnlijk weet, heeft Ruby een aantal implementaties, zoals MRI, JRuby, Rubinius, Opal, RubyMotion etc., en elk van hen kan een ander patroon van code-uitvoering gebruiken. Dit artikel richt zich op de eerste drie en vergelijkt MRI

Zoals je waarschijnlijk weet, heeft Ruby een aantal implementaties, zoals MRI, JRuby, Rubinius, Opal, RubyMotion etc., en elk van hen kan een ander patroon van code uitvoering. Dit artikel richt zich op de eerste drie en vergelijkt MRI (momenteel de populairste implementatie) met JRuby en Rubinius door een paar voorbeeldscripts uit te voeren die geschiktheid van forking en threading in verschillende situaties moeten beoordelen, zoals het verwerken van CPU-intensieve algoritmen, het kopiëren van bestanden, enzovoort.

Vork

  • is een nieuw kindproces (een kopie van het ouderproces)
  • heeft een nieuwe procesidentifier (PID)
  • heeft apart geheugen*
  • communiceert met anderen via inter-proces communicatie (IPC) kanalen zoals berichtwachtrijen, bestanden, sockets etc.
  • bestaat zelfs als ouderproces eindigt
  • is een POSIX-oproep - werkt voornamelijk op Unix-platforms

Draad

  • is "slechts" een uitvoeringscontext, werkend binnen een proces
  • deelt al het geheugen met anderen (standaard gebruikt het minder geheugen dan een fork)
  • communiceert met anderen door middel van gedeelde geheugenobjecten
  • sterft met een proces
  • introduceert typische multi-threading problemen zoals starvation, deadlocks etc.

Er zijn genoeg tools die forks en threads gebruiken en die dagelijks worden gebruikt, bijvoorbeeld Unicorn (forks) en Puma (threads) op het niveau van applicatieservers, Resque (forks) en Sidekiq (threads) op het niveau van achtergrondtaken, enzovoort.

De volgende tabel toont de ondersteuning voor forking en threading in de belangrijkste Ruby implementaties.

Ruby implementatieForkingInrijgen
MRIJaJa (beperkt door GIL**)
JRuby–Ja
RubiniusJaJa

Twee andere magische woorden komen als een boemerang terug in dit onderwerp - parallellisme en concurrency - we moeten ze een beetje uitleggen. Allereerst kunnen deze termen niet door elkaar worden gebruikt. In een notendop - we kunnen spreken van parallellisme wanneer twee of meer taken op precies hetzelfde moment worden verwerkt. Concurrency vindt plaats wanneer twee of meer taken worden verwerkt in overlappende tijdsperioden (niet noodzakelijkerwijs op hetzelfde moment). Ja, het is een brede uitleg, maar goed genoeg om je te helpen het verschil op te merken en de rest van dit artikel te begrijpen.

Fronented Rapport voor 2020

De volgende tabel toont de ondersteuning voor parallellisme en concurrency.

Ruby implementatieParallellisme (via vorken)Parallellisme (via threads)Concurrentie
MRIJaGeenJa
JRuby–JaJa
RubiniusJaJa (sinds versie 2.X)Ja

Dat is het einde van de theorie - laten we het in de praktijk zien!

  • Het hebben van apart geheugen hoeft niet te leiden tot het verbruiken van dezelfde hoeveelheid als het bovenliggende proces. Er zijn enkele geheugenoptimalisatietechnieken. Een daarvan is Copy on Write (CoW), waarmee het ouderproces toegewezen geheugen kan delen met het kind zonder het te kopiëren. Met CoW is er alleen extra geheugen nodig als het gedeelde geheugen door een kindproces wordt gewijzigd. In de Ruby context is niet elke implementatie CoW-vriendelijk, bijvoorbeeld MRI ondersteunt het volledig sinds versie 2.X. Voor deze versie verbruikte elke fork net zoveel geheugen als een ouder proces.
  • Een van de grootste voordelen/nadelen van MRI (schrap het ongepaste alternatief) is het gebruik van GIL (Global Interpreter Lock). In een notendop is dit mechanisme verantwoordelijk voor het synchroniseren van de uitvoering van threads, wat betekent dat er slechts één thread tegelijk kan worden uitgevoerd. Maar wacht... Betekent dit dat het helemaal geen zin heeft om threads te gebruiken in MRI? Het antwoord komt met het begrijpen van de GIL internals... of op zijn minst het bekijken van de codevoorbeelden in dit artikel.

Testgeval

Om te laten zien hoe forking en threading werkt in Ruby's implementaties, heb ik een eenvoudige klasse genaamd Test en een paar andere die ervan erven. Elke klasse heeft een andere taak om te verwerken. Standaard draait elke taak vier keer in een lus. Ook draait elke taak tegen drie typen van code-uitvoering: sequentieel, met forks en met threads. Bovendien, Benchmark.bmbm draait het blok code twee keer - de eerste keer om de runtime-omgeving up & running te krijgen, de tweede keer om te meten. Alle resultaten die in dit artikel worden gepresenteerd, zijn verkregen tijdens de tweede run. Natuurlijk, zelfs bmbm methode garandeert geen perfecte isolatie, maar de verschillen tussen meerdere runs van de code zijn onbeduidend.

benchmark vereisen

klasse Test
  BEDRAG = 4

  def run
    Benchmark.bmbm do |b|
      b.report("sequential") { sequential }
      b.report("forking") { forking }
      b.report("threading") { threading }
    einde
  einde

  privé

  def sequentieel
    AMOUNT.times { uitvoeren }
  einde

  def forking
    AMOUNT.times doen
      fork doen
        voer  uit
      einde
    einde

    Proces.waitall
  redding Niet geïmplementeerd => e
    # fork methode is niet beschikbaar in JRuby
    zet e
  einde

  def threading
    threads = []

    AMOUNT.times do
      threads << Thread.new do
        voer  uit
      einde
    einde

    threads.map(&:join)
  einde

  def uitvoeren
    raise "not implemented" (niet geïmplementeerd)
  einde
einde
Belastingstest

Voert berekeningen in een lus uit om grote CPU-belasting te genereren.

Klasse LoadTest < Test
  def uitvoeren
    1000.keer { 1000.keer { 2**3**4 } }
  einde
einde

Laten we het uitvoeren...

LoadTest.new.run

...en controleer de resultaten

MRIJRubyRubinius
sequentieel1.8629282.0890001.918873
forking0.945018–1.178322
inrijgen1.9139821.1070001.213315

Zoals je kunt zien, zijn de resultaten van sequentiële runs vergelijkbaar. Natuurlijk is er een klein verschil tussen de oplossingen, maar dat wordt veroorzaakt door de onderliggende implementatie van gekozen methoden in verschillende interpreters.

Forking levert in dit voorbeeld een aanzienlijke prestatiewinst op (code loopt bijna twee keer sneller).

Threading geeft vergelijkbare resultaten als forking, maar alleen voor JRuby en Rubinius. Het uitvoeren van het monster met threads op MRI kost iets meer tijd dan de sequentiële methode. Er zijn ten minste twee redenen. Ten eerste dwingt GIL sequentiële uitvoering van threads af, dus in een perfecte wereld zou de uitvoeringstijd hetzelfde moeten zijn als voor de sequentiële run, maar er treedt ook tijdverlies op voor GIL-bewerkingen (wisselen tussen threads enz.). Ten tweede is er ook wat overheadtijd nodig voor het aanmaken van threads.

Dit voorbeeld geeft ons geen antwoord op de vraag over de zin van gebruiksdraden in MRI. Laten we een ander voorbeeld bekijken.

Sluimertijd test

Voert een slaapmethode uit.

klasse SnoozeTest < Test
  def uitvoeren
    slaap 1
  einde
einde

Hier zijn de resultaten

MRIJRubyRubinius
sequentieel4.0046204.0060004.003186
forking1.022066–1.028381
inrijgen1.0015481.0040001.003642

Zoals je kunt zien, geeft elke implementatie vergelijkbare resultaten, niet alleen in de sequentiële en forking runs, maar ook in de threading runs. Dus, waarom heeft MRI dezelfde prestatiewinst als JRuby en Rubinius? Het antwoord ligt in de implementatie van slaap.

MRI's slaap methode is geïmplementeerd met rb_thread_wait_for C-functie, die een andere functie genaamd inheemse_slaap. Laten we eens kijken naar de implementatie ervan (de code is vereenvoudigd, de originele implementatie kan worden gevonden hier):

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

  GVL_UNLOCK_BEGIN();
  {
    // doe hier wat dingen
  }
  GVL_UNLOCK_END();

  thread_debug("native_sleep donen");
 }

De reden waarom deze functie belangrijk is, is dat het niet alleen de strikte Ruby context gebruikt, maar ook overschakelt naar de systeemcontext om daar enkele bewerkingen uit te voeren. In dit soort situaties heeft het Ruby proces niets te doen... Geweldig voorbeeld van tijdverspilling? Niet echt, want er is een GIL die zegt: "Niets te doen in deze thread? Laten we overschakelen naar een andere en na een tijdje hier terugkomen". Dit kan worden gedaan door de GIL te ontgrendelen en te vergrendelen met GVL_UNLOCK_BEGIN() en GVL_UNLOCK_END() functies.

De situatie wordt duidelijk, maar slaap methode is zelden nuttig. We hebben meer praktijkvoorbeelden nodig.

Bestand downloaden test

Voert een proces uit dat een bestand downloadt en opslaat.

vereisen "net/http"

klasse DownloadFileTest < Test
  Uitvoeren
    Net::HTTP.get("upload.wikimedia.org", "/wikipedia/commons/thumb/7/73/Ruby_logo.svg/2000px-Ruby_logo.svg.png")
  einde
einde

De volgende resultaten hoeven niet becommentarieerd te worden. Ze lijken erg op die van het voorbeeld hierboven.

1.003642JRubyRubinius
sequentieel0.3279800.3340000.329353
forking0.104766–0.121054
inrijgen0.0857890.0940000.088490

Een ander goed voorbeeld zou het kopiëren van bestanden of een andere I/O bewerking kunnen zijn.

Conclusies

  • Rubinius ondersteunt zowel forking als threading volledig (sinds versie 2.X, toen GIL werd verwijderd). Je code kan concurrent zijn en parallel draaien.
  • JRuby doet het goed met threads, maar ondersteunt forking helemaal niet. Parallellisme en gelijktijdigheid kunnen worden bereikt met threads.
  • MRI ondersteunt forking, maar threading wordt beperkt door de aanwezigheid van GIL. Concurrency zou kunnen worden bereikt met threads, maar alleen wanneer draaiende code buiten de Ruby interpreter context gaat (bijv. IO operaties, kernelfuncties). Er is geen manier om parallellisme te bereiken.

Verwante artikelen

Software Ontwikkeling

Bouw Toekomstbestendige Web Apps: Inzichten van The Codest's Expert Team

Ontdek hoe The Codest uitblinkt in het creëren van schaalbare, interactieve webapplicaties met geavanceerde technologieën, het leveren van naadloze gebruikerservaringen op alle platforms. Ontdek hoe onze expertise digitale transformatie en business...

DE BESTE
Software Ontwikkeling

Top 10 in Letland gevestigde bedrijven voor softwareontwikkeling

Lees meer over de beste softwareontwikkelingsbedrijven van Letland en hun innovatieve oplossingen in ons nieuwste artikel. Ontdek hoe deze technologieleiders uw bedrijf kunnen helpen verbeteren.

thecodest
Oplossingen voor ondernemingen en schaalvergroting

Essentiële Java-softwareontwikkeling: Een gids voor succesvol uitbesteden

Verken deze essentiële gids over succesvolle outsourcing Java-softwareontwikkeling om de efficiëntie te verbeteren, toegang te krijgen tot expertise en projectsucces te stimuleren met The Codest.

thecodest
Software Ontwikkeling

De ultieme gids voor outsourcing in Polen

De sterke groei van outsourcing in Polen wordt gedreven door economische, educatieve en technologische vooruitgang, die IT-groei en een bedrijfsvriendelijk klimaat stimuleert.

DeCodest
Oplossingen voor ondernemingen en schaalvergroting

De complete gids voor IT-auditmiddelen en -technieken

IT-audits zorgen voor veilige, efficiënte en compliant systemen. Lees het volledige artikel om meer te weten te komen over het belang ervan.

The Codest
Jakub Jakubowicz CTO & medeoprichter

Abonneer je op onze kennisbank en blijf op de hoogte van de expertise uit de IT-sector.

    Over ons

    The Codest - Internationaal softwareontwikkelingsbedrijf met technische hubs in Polen.

    Verenigd Koninkrijk - Hoofdkantoor

    • Kantoor 303B, 182-184 High Street North E6 2JA
      Londen, Engeland

    Polen - Lokale technologieknooppunten

    • Fabryczna kantorenpark, Aleja
      Pokoju 18, 31-564 Krakau
    • Hersenambassade, Konstruktorska
      11, 02-673 Warschau, Polen

      The Codest

    • Home
    • Over ons
    • Diensten
    • Case Studies
    • Weten hoe
    • Carrière
    • Woordenboek

      Diensten

    • Het advies
    • Software Ontwikkeling
    • Backend ontwikkeling
    • Frontend ontwikkeling
    • Staff Augmentation
    • Backend ontwikkelaars
    • Cloud Ingenieurs
    • Gegevensingenieurs
    • Andere
    • QA ingenieurs

      Bronnen

    • Feiten en fabels over samenwerken met een externe partner voor softwareontwikkeling
    • Van de VS naar Europa: Waarom Amerikaanse startups besluiten naar Europa te verhuizen
    • Tech Offshore Ontwikkelingshubs Vergelijking: Tech Offshore Europa (Polen), ASEAN (Filippijnen), Eurazië (Turkije)
    • Wat zijn de grootste uitdagingen voor CTO's en CIO's?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Gebruiksvoorwaarden website

    Copyright © 2025 door The Codest. Alle rechten voorbehouden.

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