window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(funktion () { var w = vindue if (w.LeadBooster) { console.warn('LeadBooster findes allerede') } 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 OG THREADING I RUBY - The Codest
Codest
  • Om os
  • Serviceydelser
    • Udvikling af software
      • Frontend-udvikling
      • Backend-udvikling
    • Staff Augmentation
      • Frontend-udviklere
      • Backend-udviklere
      • Dataingeniører
      • Cloud-ingeniører
      • QA-ingeniører
      • Andet
    • Det rådgivende
      • Revision og rådgivning
  • Industrier
    • Fintech og bankvirksomhed
    • E-commerce
    • Adtech
    • Sundhedsteknologi
    • Produktion
    • Logistik
    • Biler
    • IOT
  • Værdi for
    • ADMINISTRERENDE DIREKTØR
    • CTO
    • Leder af levering
  • Vores team
  • Casestudier
  • Ved hvordan
    • Blog
    • Møder
    • Webinarer
    • Ressourcer
Karriere Tag kontakt til os
  • Om os
  • Serviceydelser
    • Udvikling af software
      • Frontend-udvikling
      • Backend-udvikling
    • Staff Augmentation
      • Frontend-udviklere
      • Backend-udviklere
      • Dataingeniører
      • Cloud-ingeniører
      • QA-ingeniører
      • Andet
    • Det rådgivende
      • Revision og rådgivning
  • Værdi for
    • ADMINISTRERENDE DIREKTØR
    • CTO
    • Leder af levering
  • Vores team
  • Casestudier
  • Ved hvordan
    • Blog
    • Møder
    • Webinarer
    • Ressourcer
Karriere Tag kontakt til os
Pil tilbage GÅ TILBAGE
2016-10-06
Udvikling af software

FORKING OG THREADING I RUBY

Marek Gierlach

Som du sikkert ved, har Ruby et par implementeringer, såsom MRI, JRuby, Rubinius, Opal, RubyMotion osv., og hver af dem kan bruge et forskelligt mønster for udførelse af kode. Denne artikel vil fokusere på de tre første af dem og sammenligne MRI

Som du sikkert ved, har Ruby et par implementeringer, såsom MRI, JRuby, Rubinius, Opal, RubyMotion osv., og hver af dem kan bruge et forskelligt mønster af Kode udførelse. Denne artikel vil fokusere på de tre første af dem og sammenligne MRI (den mest populære implementering i øjeblikket) med JRuby og Rubinius ved at køre et par eksempler på scripts, som skal vurdere egnetheden af forking og threading i forskellige situationer, såsom behandling af CPU-intensive algoritmer, kopiering af filer osv.Før du begynder at "lære ved at gøre", er du nødt til at repetere et par grundlæggende termer.

Gaffel

  • er en ny underordnet proces (en kopi af den overordnede)
  • har en ny procesidentifikator (PID)
  • har separat hukommelse*
  • kommunikerer med andre via interproceskommunikationskanaler (IPC) som meddelelseskøer, filer, sockets osv.
  • eksisterer, selv når den overordnede proces slutter
  • er et POSIX-kald - fungerer primært på Unix-platforme

Tråd

  • er "kun" en udførelseskontekst, der arbejder inden for en proces
  • deler al hukommelse med andre (som standard bruger den mindre hukommelse end en gaffel)
  • kommunikerer med andre via delte hukommelsesobjekter
  • dør med en proces
  • introducerer typiske multi-threading-problemer som starvation, deadlocks osv.

Der er masser af værktøjer, som bruger forks og threads, og som bruges dagligt, f.eks. Unicorn (forks) og Puma (threads) på applikationsserverniveau, Resque (forks) og Sidekiq (threads) på baggrundsjobniveau osv.

Følgende tabel viser understøttelsen af forking og threading i de største Ruby-implementeringer.

Implementering af RubyGaffelGevindskæring
MRIJaJa (begrænset af GIL**)
JRuby–Ja
RubiniusJaJa

Endnu to magiske ord vender tilbage som en boomerang i dette emne - parallelisme og samtidighed - vi er nødt til at forklare dem lidt. Først og fremmest kan disse udtryk ikke bruges i flæng. Kort sagt kan vi tale om parallelitet, når to eller flere opgaver behandles på nøjagtig samme tid. Samtidighed finder sted, når to eller flere opgaver behandles i overlappende tidsperioder (ikke nødvendigvis på samme tid). Ja, det er en bred forklaring, men god nok til at hjælpe dig med at lægge mærke til forskellen og forstå resten af denne artikel.

Fronented-rapport for 2020

Følgende tabel viser understøttelsen af parallelisme og samtidighed.

Implementering af RubyParallelisme (via gafler)Parallelisme (via tråde)Samtidighed
MRIJaNejJa
JRuby–JaJa
RubiniusJaJa (siden version 2.X)Ja

Nu er det slut med teorien - lad os se det i praksis!

  • At have separat hukommelse betyder ikke nødvendigvis, at man bruger den samme mængde som den overordnede proces. Der findes nogle teknikker til optimering af hukommelsen. En af dem er Copy on Write (CoW), som gør det muligt for den overordnede proces at dele tildelt hukommelse med den underordnede uden at kopiere den. Med CoW er der kun brug for ekstra hukommelse i tilfælde af, at en underordnet proces ændrer den delte hukommelse. I Ruby-sammenhæng er det ikke alle implementeringer, der er CoW-venlige, f.eks. understøtter MRI det fuldt ud siden version 2.X. Før denne version brugte hver fork lige så meget hukommelse som en overordnet proces.
  • En af de største fordele/ulemper ved MRI (stryg det upassende alternativ) er brugen af GIL (Global Interpreter Lock). Kort fortalt er denne mekanisme ansvarlig for at synkronisere udførelsen af tråde, hvilket betyder, at kun én tråd kan udføres ad gangen. Men vent ... Betyder det, at der slet ikke er nogen grund til at bruge tråde i MRI? Svaret kommer med forståelsen af GIL's interne funktioner ... eller i det mindste ved at kigge på kodeeksemplerne i denne artikel.

Testtilfælde

For at vise, hvordan forking og threading fungerer i Rubys implementeringer, har jeg lavet en simpel klasse, der hedder Test og et par andre, der arver fra den. Hver klasse har en forskellig opgave, der skal behandles. Som standard kører hver opgave fire gange i et loop. Desuden kører hver opgave mod tre typer af kodeudførelse: sekventiel, med forks og med threads. Og derudover, Benchmark.bmbm kører kodeblokken to gange - første gang for at få runtime-miljøet op at køre, anden gang for at måle. Alle de resultater, der præsenteres i denne artikel, blev opnået i den anden kørsel. Selvfølgelig er selv bmbm Metoden garanterer ikke perfekt isolation, men forskellene mellem flere kodekørsler er ubetydelige.

kræver "benchmark"

klasse Test
  AMOUNT = 4

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

  privat

  def sekventiel
    AMOUNT.times { udfør }
  slut

  def forking
    AMOUNT.times do
      gaffel do
        udfør
      end
    end

    Process.waitall
  redder NotImplementedError => e
    # fork-metoden er ikke tilgængelig i JRuby
    sætter e
  slut

  def threading
    tråde = []

    AMOUNT.times do
      tråde << Tråd.ny do
        udfør
      end
    slut

    threads.map(&:join)
  slut

  def udføre
    raise "ikke implementeret"
  end
slut
Belastningstest

Kører beregninger i en løkke for at generere stor CPU-belastning.

klasse LoadTest < Test
  def udføre
    1000.gange { 1000.gange { 2**3**4 } }
  end
slut

Lad os køre det...

LoadTest.new.run

... og tjekke resultaterne

MRIJRubyRubinius
sekventiel1.8629282.0890001.918873
Gaffel0.945018–1.178322
gevindskæring1.9139821.1070001.213315

Som du kan se, er resultaterne fra sekventielle kørsler ens. Der er selvfølgelig en lille forskel mellem løsningerne, men det skyldes den underliggende implementering af de valgte metoder i de forskellige fortolkere.

I dette eksempel giver forking en betydelig præstationsgevinst (koden kører næsten to gange hurtigere).

Threading giver de samme resultater som forking, men kun for JRuby og Rubinius. At køre prøven med tråde på MRI bruger lidt mere tid end den sekventielle metode. Det er der mindst to grunde til. For det første tvinger GIL sekventiel udførelse af tråde, så i en perfekt verden burde udførelsestiden være den samme som for den sekventielle kørsel, men der sker også et tab af tid til GIL-operationer (skift mellem tråde osv.). For det andet er der også brug for noget overheadtid til at oprette tråde.

Dette eksempel giver os ikke svar på spørgsmålet om betydningen af brugstråde i MRI. Lad os se et andet.

Snooze-test

Kører en sleep-metode.

klasse SnoozeTest < Test
  def udføre
    sove 1
  slut
slut

Her er resultaterne

MRIJRubyRubinius
sekventiel4.0046204.0060004.003186
Gaffel1.022066–1.028381
gevindskæring1.0015481.0040001.003642

Som du kan se, giver hver implementering lignende resultater, ikke kun i de sekventielle og forgrenede kørsler, men også i de trådede. Så hvorfor har MRI den samme præstationsgevinst som JRuby og Rubinius? Svaret ligger i implementeringen af sove.

MR-scanninger sove metoden er implementeret med rb_thread_wait_for C-funktion, som bruger en anden, der hedder indfødt_søvn. Lad os tage et hurtigt kig på implementeringen (koden blev forenklet, den oprindelige implementering kan findes her):

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

  GVL_UNLOCK_BEGIN();
  {
    // gør nogle ting her
  }
  GVL_UNLOCK_END();

  thread_debug("native_sleep donen");
 }

Grunden til, at denne funktion er vigtig, er, at den ud over at bruge en streng Ruby-kontekst også skifter til systemkonteksten for at udføre nogle operationer der. I situationer som denne har Ruby-processen intet at gøre ... Et godt eksempel på tidsspilde? Ikke rigtig, for der er en GIL, der siger: "Intet at gøre i denne tråd? Lad os skifte til en anden og komme tilbage hertil efter et stykke tid". Det kan gøres ved at låse op og låse GIL med GVL_UNLOCK_BEGIN() og GVL_UNLOCK_END() funktioner.

Situationen bliver klar, men sove metoden er sjældent brugbar. Vi har brug for flere eksempler fra det virkelige liv.

Test af download af filer

Kører en proces, som downloader og gemmer en fil.

kræver "net/http"

klassen DownloadFileTest < Test
  def udfør
    Net::HTTP.get("upload.wikimedia.org", "/wikipedia/commons/thumb/7/73/Ruby_logo.svg/2000px-Ruby_logo.svg.png")
  slut
slut

Der er ingen grund til at kommentere de følgende resultater. De ligner meget dem fra eksemplet ovenfor.

1.003642JRubyRubinius
sekventiel0.3279800.3340000.329353
Gaffel0.104766–0.121054
gevindskæring0.0857890.0940000.088490

Et andet godt eksempel kunne være filkopieringsprocessen eller enhver anden I/O-operation.

Konklusioner

  • Rubinius understøtter fuldt ud både forking og threading (siden version 2.X, hvor GIL blev fjernet). Din kode kan være concurrent og køre parallelt.
  • JRuby gør et godt stykke arbejde med tråde, men understøtter slet ikke forking. Parallelisme og samtidighed kan opnås med tråde.
  • MRI understøtter forking, men threading er begrænset af tilstedeværelsen af GIL. Samtidighed kan opnås med tråde, men kun når den kørende kode går uden for Ruby-fortolkerens kontekst (f.eks. IO-operationer, kernefunktioner). Der er ingen måde at opnå parallelitet på.

Relaterede artikler

Udvikling af software

Byg fremtidssikrede webapps: Indsigt fra The Codest's ekspertteam

Oplev, hvordan The Codest udmærker sig ved at skabe skalerbare, interaktive webapplikationer med banebrydende teknologier, der leverer sømløse brugeroplevelser på tværs af alle platforme. Lær, hvordan vores ekspertise driver digital transformation og...

DENKODEST
Udvikling af software

Top 10 Letlands-baserede softwareudviklingsvirksomheder

Læs om Letlands bedste softwareudviklingsvirksomheder og deres innovative løsninger i vores seneste artikel. Find ud af, hvordan disse teknologiledere kan hjælpe med at løfte din virksomhed.

thecodest
Løsninger til virksomheder og scaleups

Grundlæggende om Java-softwareudvikling: En guide til succesfuld outsourcing

Udforsk denne vigtige guide til vellykket outsourcing af Java-softwareudvikling for at forbedre effektiviteten, få adgang til ekspertise og skabe projektsucces med The Codest.

thecodest
Udvikling af software

Den ultimative guide til outsourcing i Polen

Den voldsomme stigning i outsourcing i Polen er drevet af økonomiske, uddannelsesmæssige og teknologiske fremskridt, der fremmer it-vækst og et erhvervsvenligt klima.

TheCodest
Løsninger til virksomheder og scaleups

Den komplette guide til IT-revisionsværktøjer og -teknikker

IT-revisioner sikrer sikre, effektive og kompatible systemer. Lær mere om deres betydning ved at læse hele artiklen.

Codest
Jakub Jakubowicz CTO og medstifter

Tilmeld dig vores vidensbase, og hold dig opdateret om ekspertisen fra it-sektoren.

    Om os

    The Codest - International softwareudviklingsvirksomhed med tech-hubs i Polen.

    Storbritannien - Hovedkvarter

    • Kontor 303B, 182-184 High Street North E6 2JA
      London, England

    Polen - Lokale teknologiske knudepunkter

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Hjerneambassaden, Konstruktorska
      11, 02-673 Warszawa, Polen

      Codest

    • Hjem
    • Om os
    • Serviceydelser
    • Casestudier
    • Ved hvordan
    • Karriere
    • Ordbog

      Serviceydelser

    • Det rådgivende
    • Udvikling af software
    • Backend-udvikling
    • Frontend-udvikling
    • Staff Augmentation
    • Backend-udviklere
    • Cloud-ingeniører
    • Dataingeniører
    • Andet
    • QA-ingeniører

      Ressourcer

    • Fakta og myter om at samarbejde med en ekstern softwareudviklingspartner
    • Fra USA til Europa: Hvorfor beslutter amerikanske startups sig for at flytte til Europa?
    • Sammenligning af Tech Offshore-udviklingsknudepunkter: Tech Offshore Europa (Polen), ASEAN (Filippinerne), Eurasien (Tyrkiet)
    • Hvad er de største udfordringer for CTO'er og CIO'er?
    • Codest
    • Codest
    • Codest
    • Privacy policy
    • Vilkår for brug af hjemmesiden

    Copyright © 2025 af The Codest. Alle rettigheder forbeholdes.

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