window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster υπάρχει ήδη') } 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 ΚΑΙ THREADING ΣΤΟ RUBY - The Codest
The Codest
  • Σχετικά με εμάς
  • Υπηρεσίες
    • Ανάπτυξη λογισμικού
      • Ανάπτυξη Frontend
      • Backend Ανάπτυξη
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Μηχανικοί δεδομένων
      • Μηχανικοί cloud
      • Μηχανικοί QA
      • Άλλα
    • Συμβουλευτική
      • Έλεγχος & Συμβουλευτική
  • Βιομηχανίες
    • Fintech & Τραπεζική
    • E-commerce
    • Adtech
    • Healthtech
    • Κατασκευή
    • Εφοδιαστική
    • Αυτοκίνητο
    • IOT
  • Αξία για
    • CEO
    • CTO
    • Διευθυντής παράδοσης
  • Η ομάδα μας
  • Case Studies
  • Μάθετε πώς
    • Blog
    • Συναντήσεις
    • Διαδικτυακά σεμινάρια
    • Πόροι
Καριέρα Ελάτε σε επαφή
  • Σχετικά με εμάς
  • Υπηρεσίες
    • Ανάπτυξη λογισμικού
      • Ανάπτυξη Frontend
      • Backend Ανάπτυξη
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Μηχανικοί δεδομένων
      • Μηχανικοί cloud
      • Μηχανικοί QA
      • Άλλα
    • Συμβουλευτική
      • Έλεγχος & Συμβουλευτική
  • Αξία για
    • CEO
    • CTO
    • Διευθυντής παράδοσης
  • Η ομάδα μας
  • Case Studies
  • Μάθετε πώς
    • Blog
    • Συναντήσεις
    • Διαδικτυακά σεμινάρια
    • Πόροι
Καριέρα Ελάτε σε επαφή
Πίσω βέλος GO BACK
2016-10-06
Ανάπτυξη λογισμικού

FORKING ΚΑΙ THREADING ΣΤΟ RUBY

Marek Gierlach

Όπως πιθανώς γνωρίζετε, η Ruby έχει αρκετές υλοποιήσεις, όπως οι MRI, JRuby, Rubinius, Opal, RubyMotion κ.λπ., και κάθε μία από αυτές μπορεί να χρησιμοποιεί διαφορετικό μοτίβο εκτέλεσης κώδικα. Αυτό το άρθρο θα επικεντρωθεί στις τρεις πρώτες από αυτές και θα συγκρίνει την MRI

Όπως πιθανώς γνωρίζετε, η Ruby έχει μερικές υλοποιήσεις, όπως οι MRI, JRuby, Rubinius, Opal, RubyMotion κ.α., και κάθε μία από αυτές μπορεί να χρησιμοποιεί ένα διαφορετικό μοτίβο της κωδικός εκτέλεση. Αυτό το άρθρο θα επικεντρωθεί στις τρεις πρώτες από αυτές και θα συγκρίνει την MRI (την πιο δημοφιλή υλοποίηση αυτή τη στιγμή) με την JRuby και την Rubinius εκτελώντας μερικά σενάρια-δείγματα, τα οποία υποτίθεται ότι αξιολογούν την καταλληλότητα του forking και του threading σε διάφορες καταστάσεις, όπως η επεξεργασία αλγορίθμων έντασης CPU, η αντιγραφή αρχείων κ.λπ.

Πιρούνι

  • είναι μια νέα διεργασία-παιδί (αντίγραφο της μητρικής)
  • έχει νέο αναγνωριστικό διεργασίας (PID)
  • έχει ξεχωριστή μνήμη*
  • επικοινωνεί με άλλους μέσω καναλιών επικοινωνίας μεταξύ διεργασιών (IPC), όπως ουρές μηνυμάτων, αρχεία, υποδοχές κ.λπ.
  • υπάρχει ακόμη και όταν η γονική διαδικασία τερματίζεται
  • είναι μια κλήση POSIX - λειτουργεί κυρίως σε πλατφόρμες Unix

Νήμα

  • είναι "μόνο" ένα πλαίσιο εκτέλεσης, που λειτουργεί στο πλαίσιο μιας διεργασίας
  • μοιράζεται όλη τη μνήμη με άλλους (από προεπιλογή χρησιμοποιεί λιγότερη μνήμη από ένα fork)
  • επικοινωνεί με άλλους μέσω αντικειμένων κοινής μνήμης
  • πεθαίνει με μια διαδικασία
  • εισάγει τυπικά προβλήματα πολλαπλών νημάτων, όπως πείνα, αδιέξοδα κ.λπ.

Υπάρχουν πολλά εργαλεία που χρησιμοποιούν forks και threads, τα οποία χρησιμοποιούνται σε καθημερινή βάση, π.χ. Unicorn (forks) και Puma (threads) σε επίπεδο διακομιστών εφαρμογών, Resque (forks) και Sidekiq (threads) σε επίπεδο εργασιών παρασκηνίου, κ.λπ.

Ο παρακάτω πίνακας παρουσιάζει την υποστήριξη forking και threading στις κυριότερες υλοποιήσεις της Ruby.

Υλοποίηση RubyΔιακλάδωσηΣπείρωμα
ΜΑΓΝΗΤΙΚΉ ΤΟΜΟΓΡΑΦΊΑΝαιΝαι (περιορίζεται από το GIL**)
JRuby–Ναι
RubiniusΝαιΝαι

Δύο ακόμη μαγικές λέξεις επιστρέφουν σαν μπούμερανγκ σε αυτό το θέμα - ο παραλληλισμός και ο συγχρονισμός - πρέπει να τις εξηγήσουμε λίγο. Πρώτα απ' όλα, οι όροι αυτοί δεν μπορούν να χρησιμοποιούνται εναλλακτικά. Με λίγα λόγια - μπορούμε να μιλάμε για παραλληλισμό όταν δύο ή περισσότερες εργασίες υποβάλλονται σε επεξεργασία ακριβώς την ίδια στιγμή. Η ταυτόχρονη εκτέλεση λαμβάνει χώρα όταν δύο ή περισσότερες εργασίες υποβάλλονται σε επεξεργασία σε επικαλυπτόμενες χρονικές περιόδους (όχι απαραίτητα ταυτόχρονα). Ναι, είναι μια ευρεία εξήγηση, αλλά αρκετά καλή για να σας βοηθήσει να παρατηρήσετε τη διαφορά και να κατανοήσετε το υπόλοιπο του άρθρου.

Έκθεση Fronented για το 2020

Στον παρακάτω πίνακα παρουσιάζεται η υποστήριξη παραλληλισμού και ταυτόχρονης εκτέλεσης.

Υλοποίηση RubyΠαραλληλισμός (μέσω διακλαδώσεων)Παραλληλισμός (μέσω νημάτων)Παράλληλη χρήση
ΜΑΓΝΗΤΙΚΉ ΤΟΜΟΓΡΑΦΊΑΝαιΌχιΝαι
JRuby–ΝαιΝαι
RubiniusΝαιΝαι (από την έκδοση 2.X)Ναι

Τελειώσαμε με τη θεωρία - ας το δούμε στην πράξη!

  • Η ξεχωριστή μνήμη δεν είναι απαραίτητο να καταναλώνει την ίδια ποσότητα μνήμης με τη μητρική διεργασία. Υπάρχουν ορισμένες τεχνικές βελτιστοποίησης της μνήμης. Μία από αυτές είναι η Copy on Write (CoW), η οποία επιτρέπει στη γονική διεργασία να μοιράζεται την κατανεμημένη μνήμη με τη θυγατρική χωρίς να την αντιγράφει. Με την CoW πρόσθετη μνήμη απαιτείται μόνο στην περίπτωση τροποποίησης της κοινόχρηστης μνήμης από μια διεργασία-παιδί. Στο πλαίσιο της Ruby, δεν είναι όλες οι υλοποιήσεις φιλικές προς το CoW, π.χ. η MRI το υποστηρίζει πλήρως από την έκδοση 2.X. Πριν από αυτή την έκδοση κάθε fork κατανάλωνε τόση μνήμη όση και μια γονική διεργασία.
  • Ένα από τα μεγαλύτερα πλεονεκτήματα/μειονεκτήματα της μαγνητικής τομογραφίας (διαγράψτε την ακατάλληλη εναλλακτική λύση) είναι η χρήση του GIL (Global Interpreter Lock). Με λίγα λόγια, ο μηχανισμός αυτός είναι υπεύθυνος για τον συγχρονισμό της εκτέλεσης των νημάτων, πράγμα που σημαίνει ότι μόνο ένα νήμα μπορεί να εκτελείται κάθε φορά. Αλλά περιμένετε... Μήπως αυτό σημαίνει ότι δεν υπάρχει κανένας λόγος να χρησιμοποιούμε νήματα στην MRI; Η απάντηση έρχεται με την κατανόηση των εσωτερικών στοιχείων του GIL... ή τουλάχιστον ρίχνοντας μια ματιά στα δείγματα κώδικα σε αυτό το άρθρο.

Περίπτωση δοκιμής

Προκειμένου να παρουσιάσω πώς λειτουργεί η διακλάδωση και το threading στις υλοποιήσεις της Ruby, δημιούργησα μια απλή κλάση που ονομάζεται Δοκιμή και μερικά άλλα που κληρονομούν από αυτό. Κάθε κλάση έχει μια διαφορετική εργασία να επεξεργαστεί. Από προεπιλογή, κάθε εργασία εκτελείται τέσσερις φορές σε έναν βρόχο. Επίσης, κάθε εργασία εκτελείται έναντι τριών τύπων εκτέλεσης κώδικα: διαδοχική, με διακλαδώσεις και με νήματα. Επιπλέον, Benchmark.bmbm εκτελεί το μπλοκ κώδικα δύο φορές - την πρώτη φορά για να τεθεί σε λειτουργία το περιβάλλον χρόνου εκτέλεσης και τη δεύτερη φορά για να μετρήσει. Όλα τα αποτελέσματα που παρουσιάζονται σε αυτό το άρθρο προέκυψαν κατά τη δεύτερη εκτέλεση. Φυσικά, ακόμη και bmbm μέθοδος δεν εγγυάται τέλεια απομόνωση, αλλά οι διαφορές μεταξύ πολλαπλών εκτελέσεων κώδικα είναι ασήμαντες.

απαιτούν "benchmark"

κλάση Test
  AMOUNT = 4

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

  private

  def sequential
    AMOUNT.times { perform }
  end

  def forking
    AMOUNT.times do
      fork do
        perform
      end
    end

    Process.waitall
  rescue NotImplementedError => e
    # Η μέθοδος fork δεν είναι διαθέσιμη στο JRuby
    puts e
  end

  def threading
    threads = []

    AMOUNT.times do
      threads << Thread.new do
        perform
      end
    end

    threads.map(&:join)
  end

  def perform
    raise "not implemented"
  end
end
Δοκιμή φορτίου

Εκτελεί υπολογισμούς σε βρόχο για να δημιουργήσει μεγάλο φορτίο CPU.

class LoadTest < Δοκιμή
  def perform
    1000.times { 1000.times { 2**3**4 } }
  end
end

Ας το τρέξουμε...

LoadTest.new.run

...και ελέγξτε τα αποτελέσματα

ΜΑΓΝΗΤΙΚΉ ΤΟΜΟΓΡΑΦΊΑJRubyRubinius
διαδοχική1.8629282.0890001.918873
διχάλα0.945018–1.178322
σπείρωμα1.9139821.1070001.213315

Όπως μπορείτε να δείτε, τα αποτελέσματα από τις διαδοχικές εκτελέσεις είναι παρόμοια. Φυσικά υπάρχει μια μικρή διαφορά μεταξύ των λύσεων, αλλά αυτή οφείλεται στην υποκείμενη υλοποίηση των επιλεγμένων μεθόδων στους διάφορους διερμηνείς.

Η διακλάδωση, σε αυτό το παράδειγμα, έχει σημαντικό κέρδος απόδοσης (ο κώδικας εκτελείται σχεδόν δύο φορές ταχύτερα).

Το threading δίνει παρόμοια αποτελέσματα με το forking, αλλά μόνο για τα JRuby και Rubinius. Η εκτέλεση του δείγματος με νήματα στην MRI καταναλώνει λίγο περισσότερο χρόνο από τη διαδοχική μέθοδο. Υπάρχουν τουλάχιστον δύο λόγοι. Πρώτον, η GIL επιβάλλει την εκτέλεση με διαδοχικά νήματα, επομένως σε έναν τέλειο κόσμο ο χρόνος εκτέλεσης θα έπρεπε να είναι ο ίδιος με αυτόν της διαδοχικής εκτέλεσης, αλλά υπάρχει επίσης απώλεια χρόνου για τις λειτουργίες της GIL (εναλλαγή μεταξύ νημάτων κ.λπ.). Δεύτερον, απαιτείται επίσης κάποιος χρόνος επιβάρυνσης για τη δημιουργία νημάτων.

Αυτό το παράδειγμα δεν μας δίνει απάντηση στο ερώτημα σχετικά με το νόημα των νημάτων χρήσης στη μαγνητική τομογραφία. Ας δούμε ένα άλλο παράδειγμα.

Δοκιμή Snooze

Εκτελεί μια μέθοδο ύπνου.

class SnoozeTest < Δοκιμή
  def perform
    sleep 1
  end
end

Ακολουθούν τα αποτελέσματα

ΜΑΓΝΗΤΙΚΉ ΤΟΜΟΓΡΑΦΊΑJRubyRubinius
διαδοχική4.0046204.0060004.003186
διχάλα1.022066–1.028381
σπείρωμα1.0015481.0040001.003642

Όπως μπορείτε να δείτε, κάθε υλοποίηση δίνει παρόμοια αποτελέσματα όχι μόνο στις διαδοχικές και διακλαδικές εκτελέσεις, αλλά και στις εκτελέσεις με νήματα. Επομένως, γιατί το MRI έχει το ίδιο κέρδος απόδοσης με το JRuby και το Rubinius; Η απάντηση βρίσκεται στην υλοποίηση της ύπνος.

Μαγνητικές τομογραφίες ύπνος η μέθοδος υλοποιείται με την rb_thread_wait_for C, η οποία χρησιμοποιεί μια άλλη συνάρτηση που ονομάζεται native_sleep. Ας ρίξουμε μια γρήγορη ματιά στην υλοποίησή του (ο κώδικας απλοποιήθηκε, η αρχική υλοποίηση μπορεί να βρεθεί εδώ):

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

  GVL_UNLOCK_BEGIN(),
  {
    // do some stuff here
  }
  GVL_UNLOCK_END(),

  thread_debug("native_sleep donen"),
 }

Ο λόγος για τον οποίο αυτή η συνάρτηση είναι σημαντική είναι ότι εκτός από τη χρήση του αυστηρού πλαισίου Ruby, μεταβαίνει και στο πλαίσιο του συστήματος για να εκτελέσει ορισμένες λειτουργίες εκεί. Σε τέτοιες περιπτώσεις, η διεργασία Ruby δεν έχει τίποτα να κάνει... Μεγάλο παράδειγμα σπατάλης χρόνου; Όχι ακριβώς, γιατί υπάρχει ένα GIL που λέει: "Δεν υπάρχει τίποτα να κάνετε σε αυτό το νήμα; Ας αλλάξουμε σε ένα άλλο και ας επιστρέψουμε εδώ μετά από λίγο". Αυτό θα μπορούσε να γίνει με το ξεκλείδωμα και το κλείδωμα του GIL με GVL_UNLOCK_BEGIN() και GVL_UNLOCK_END() λειτουργίες.

Η κατάσταση γίνεται σαφής, αλλά ύπνος μέθοδος είναι σπάνια χρήσιμη. Χρειαζόμαστε περισσότερα παραδείγματα από την πραγματική ζωή.

Δοκιμή λήψης αρχείων

Εκτελεί μια διαδικασία που κατεβάζει και αποθηκεύει ένα αρχείο.

απαιτούν "net/http"

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

Δεν χρειάζεται να σχολιαστούν τα ακόλουθα αποτελέσματα. Είναι αρκετά παρόμοια με εκείνα του παραπάνω παραδείγματος.

1.003642JRubyRubinius
διαδοχική0.3279800.3340000.329353
διχάλα0.104766–0.121054
σπείρωμα0.0857890.0940000.088490

Ένα άλλο καλό παράδειγμα θα μπορούσε να είναι η διαδικασία αντιγραφής αρχείων ή οποιαδήποτε άλλη λειτουργία εισόδου/εξόδου.

Συμπεράσματα

  • Rubinius υποστηρίζει πλήρως τόσο το forking όσο και το threading (από την έκδοση 2.X, όταν καταργήθηκε το GIL). Ο κώδικάς σας θα μπορούσε να είναι ταυτόχρονος και να εκτελείται παράλληλα.
  • JRuby κάνει καλή δουλειά με τα νήματα, αλλά δεν υποστηρίζει καθόλου το forking. Ο παραλληλισμός και ο συγχρονισμός θα μπορούσαν να επιτευχθούν με νήματα.
  • ΜΑΓΝΗΤΙΚΉ ΤΟΜΟΓΡΑΦΊΑ υποστηρίζει διακλάδωση, αλλά το threading περιορίζεται από την παρουσία του GIL. Ο συγχρονισμός θα μπορούσε να επιτευχθεί με νήματα, αλλά μόνο όταν ο εκτελούμενος κώδικας βγαίνει έξω από το πλαίσιο του διερμηνευτή Ruby (π.χ. λειτουργίες IO, λειτουργίες πυρήνα). Δεν υπάρχει τρόπος να επιτευχθεί παραλληλισμός.

Σχετικά άρθρα

Ανάπτυξη λογισμικού

Κατασκευάστε μελλοντικά ασφαλείς εφαρμογές Web: γνώσεις από την ομάδα εμπειρογνωμόνων του The Codest

Ανακαλύψτε πώς η The Codest υπερέχει στη δημιουργία κλιμακούμενων, διαδραστικών εφαρμογών ιστού με τεχνολογίες αιχμής, παρέχοντας απρόσκοπτη εμπειρία χρήστη σε όλες τις πλατφόρμες. Μάθετε πώς η τεχνογνωσία μας οδηγεί στον ψηφιακό μετασχηματισμό και την επιχειρηματική...

THECODEST
Ανάπτυξη λογισμικού

Top 10 εταιρείες ανάπτυξης λογισμικού με έδρα τη Λετονία

Μάθετε για τις κορυφαίες εταιρείες ανάπτυξης λογισμικού της Λετονίας και τις καινοτόμες λύσεις τους στο τελευταίο μας άρθρο. Ανακαλύψτε πώς αυτοί οι τεχνολογικοί ηγέτες μπορούν να βοηθήσουν στην ανύψωση της επιχείρησής σας.

thecodest
Λύσεις Enterprise & Scaleups

Βασικά στοιχεία ανάπτυξης λογισμικού Java: Α Guide to Outsourcing Successfully (Οδηγός για την επιτυχή εξωτερική ανάθεση)

Εξερευνήστε αυτόν τον βασικό οδηγό για την επιτυχή ανάπτυξη λογισμικού outsourcing Java για να αυξήσετε την αποδοτικότητα, να αποκτήσετε πρόσβαση στην τεχνογνωσία και να οδηγήσετε την επιτυχία των έργων με The Codest.

thecodest
Ανάπτυξη λογισμικού

Ο απόλυτος οδηγός για το Outsourcing στην Πολωνία

Η έξαρση της outsourcing στην Πολωνία οφείλεται στις οικονομικές, εκπαιδευτικές και τεχνολογικές εξελίξεις, που ευνοούν την ανάπτυξη της πληροφορικής και το φιλικό προς τις επιχειρήσεις κλίμα.

TheCodest
Λύσεις Enterprise & Scaleups

Ο πλήρης οδηγός εργαλείων και τεχνικών ελέγχου πληροφορικής

Οι έλεγχοι ΤΠ διασφαλίζουν ασφαλή, αποτελεσματικά και συμβατά συστήματα. Μάθετε περισσότερα για τη σημασία τους διαβάζοντας ολόκληρο το άρθρο.

The Codest
Jakub Jakubowicz CTO & Συνιδρυτής

Εγγραφείτε στη βάση γνώσεών μας και μείνετε ενήμεροι για την τεχνογνωσία από τον τομέα της πληροφορικής.

    Σχετικά με εμάς

    The Codest - Διεθνής εταιρεία ανάπτυξης λογισμικού με κέντρα τεχνολογίας στην Πολωνία.

    Ηνωμένο Βασίλειο - Έδρα

    • Γραφείο 303B, 182-184 High Street North E6 2JA
      Λονδίνο, Αγγλία

    Πολωνία - Τοπικοί κόμβοι τεχνολογίας

    • Πάρκο γραφείων Fabryczna, Aleja
      Pokoju 18, 31-564 Κρακοβία
    • Πρεσβεία του εγκεφάλου, Konstruktorska
      11, 02-673 Βαρσοβία, Πολωνία

      The Codest

    • Αρχική σελίδα
    • Σχετικά με εμάς
    • Υπηρεσίες
    • Case Studies
    • Μάθετε πώς
    • Καριέρα
    • Λεξικό

      Υπηρεσίες

    • Συμβουλευτική
    • Ανάπτυξη λογισμικού
    • Backend Ανάπτυξη
    • Ανάπτυξη Frontend
    • Staff Augmentation
    • Backend Developers
    • Μηχανικοί cloud
    • Μηχανικοί δεδομένων
    • Άλλα
    • Μηχανικοί QA

      Πόροι

    • Γεγονότα και μύθοι σχετικά με τη συνεργασία με εξωτερικό συνεργάτη ανάπτυξης λογισμικού
    • Από τις ΗΠΑ στην Ευρώπη: Γιατί οι αμερικανικές νεοσύστατες επιχειρήσεις αποφασίζουν να μετεγκατασταθούν στην Ευρώπη
    • Σύγκριση υπεράκτιων κόμβων ανάπτυξης τεχνολογίας: Ευρώπη (Πολωνία), ASEAN (Φιλιππίνες), Ευρασία (Τουρκία)
    • Ποιες είναι οι κορυφαίες προκλήσεις των CTOs και των CIOs;
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Πνευματικά δικαιώματα © 2025 από The Codest. Όλα τα δικαιώματα διατηρούνται.

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