Πρόσφατα, διάβασα αρκετές συγκρίσεις του Node.js με το Golang. Δυστυχώς, η αναζήτηση στο Google για αυτό το θέμα κυριαρχείται από, αχ, όχι τόσο ενημερωμένες απόψεις – με πολλούς ξεκάθαρους γελοίους ισχυρισμούς. Και ενώ η επανάληψη γελοίων ισχυρισμών ξανά και ξανά δεν τον κάνει λιγότερο γελοίο – σίγουρα μπορεί να σχηματίσει μια εντελώς λανθασμένη γνώμη μεταξύ των προγραμματιστών και ακόμη χειρότερα μεταξύ των αρμοδίων για τη λήψη αποφάσεων. Για να βοηθήσω στην αντιμετώπισή του, έφτιαξα μια συλλογή από τους πιο δημοφιλείς/πιο γελοίους μύθους για το Node και το Golang – για να τους καταρρίψω εδώ.
Μύθος #1: Το Node.js είναι μονού νήματος, επομένως δεν κλιμακώνεται
„εάν προγραμματιστής κάνει θέλει να πάει πολυπύρηνο, το μόνο που έχει να κάνει είναι να το χρησιμοποιήσει γορουτίνες (στο Golang) , ή σύμπλεγμα ενότητα (στον Κόμβο)Πιθανώς το πιο δημοφιλές θανάσιμο λάθος σημείο που διαφημίζουν οι ανταγωνιστές του Node.js είναι ότι, όπως [Sathananthan]1 γράφει περήφανα, „Το Node.js είναι μονού νήματος, πράγμα που σημαίνει ότι οι οδηγίες εκτέλεσης εκτελούνται με τη σειρά.“
Αλήθεια Νο 1: Αν το κάνει ο προγραμματιστής τίποτα να εκμεταλλευτεί το multi-coreing στο πρόγραμμά της, και τα δυο golang και Το Node.js είναι μονού νήματος. Ωστόσο, εάν προγραμματιστής κάνει θέλει να πάει πολυπύρηνα, πρέπει να χρησιμοποιήσει γορουτίνες (στο Golang) , ή σύμπλεγμα ενότητα (στον Κόμβο). Το μόνο που μπορεί να θέλει κανείς να υποστηρίξει ότι η προσέγγιση του golang είναι καλύτερα – ωστόσο, αυτό είναι τουλάχιστον αμφισβητήσιμο (περισσότερα στον Μύθο #3 και στον Μύθο #4 παρακάτω).
1 ένα άρθρο που πήρε #1 τοποθέτηση και το „απόσπασμα λειτουργιών“ της Google στην αναζήτηση „Node.js vs golang“, είναι ένα άρθρο από μια αδιαμφισβήτητη αρχή στον χώρο ένας τύπος που γράφει το έργο του 3ου εξαμήνου. Μπράβο, Google!
Μύθος #2: Το Node.js ερμηνεύεται
Πολλές πηγές συζητούν τη φερόμενη ερμηνευτική φύση της JavaScript. δες, για παράδειγμα, [Harkushko]: „Αλλά επειδή η JavaScript είναι μια ερμηνευμένη γλώσσα, χρειάζεται περισσότερος χρόνος για την εκτέλεση κώδικα που είναι γραμμένος στο Node.js από ό,τι για την εκτέλεση κώδικα γραμμένου στο Go.“
Αλήθεια Νο 2: Ενώ η απόδοση του Node.js είναι πράγματι συνήθως χαμηλότερο από αυτό του Go, δεν έχει καμία σχέση με τη γλώσσα που ερμηνεύεται (το JavaScript ως προδιαγραφή δεν χρειάζεται να ερμηνευτεί ή να μεταγλωττιστεί. συγκεκριμένη εφαρμογή το οποίο ερμηνεύεται ή μεταγλωττίζεται και ο κινητήρας Node.js v8 διαθέτει μεταγλωττιστή JIT). Αυτό επιβεβαιώνεται από μια παρατήρηση ότι η διαφορά απόδοσης μεταξύ των δύο είναι σχετικά μικρή (βλ. Μύθο #5 παρακάτω).
Μύθος #3: Οι γορουτίνες είναι ο τρόπος για την κλίμακα
„Τα πράγματα που βασίζονται σε mutex είναι εγγενώς μη δοκιμασμέναΠολύ συχνά αναφέρεται ότι το Golang έχει σχεδιαστεί για επεκτασιμότητα. μόνο ως ένα παράδειγμα, η ίδια εξαιρετικά υψηλή βαθμολογία από την Google [Sathananthan] λέει „Το Golang σχεδιάστηκε πραγματικά για επεκτασιμότητα και ταυτόχρονη χρήση, χωρίς πολλή ταλαιπωρία.“ Αυτό συνήθως συνοδεύεται από κάποια διατύπωση σχετικά με τον συγχρονισμό που βασίζεται σε γορουτίνες, υπονοώντας τουλάχιστον ότι η επεκτασιμότητα μπορεί/πρέπει να επιτευχθεί με τις γορουτίνες.
Αλήθεια Νο 3: Στην πραγματικότητα, οι γορουτίνες έχουν ένα δυνητικό πραγματικά μεγάλο πρόβλημα(tm) όσον αφορά την επεκτασιμότητα – καθώς οι γορουτίνες DO επιτρέπουν την πρόσβαση σε μη σταθερές καθολικές μεταβλητές, αυτό όχι μόνο ανοίγει ολόκληρο το κουτί των σκουληκιών ως προς την αξιοπιστία (γιατί, πράγματα που βασίζονται σε mutex είναι εγγενώς ανεξέλεγκτη!2), αλλά περιορίζει επίσης την επεκτασιμότητα σε ένα μεμονωμένο κουτί (Επιπλέον, τα συστήματα Κοινόχρηστης Μνήμης ΠΡΟΚΥΠΤΟΥΝ την επεκτασιμότητα ακόμη και σε μεμονωμένο κουτί· αυτό έχει πολλούς λόγους που κυμαίνονται από τον νόμο του Amdahl έως περιττές ακυρώσεις της προσωρινής μνήμης λόγω διακόπτες περιβάλλοντος νήματος που προκαλούνται από mutex). Ομολογουμένως, οι τρέχουσες πρακτικές golang ενθαρρύνουν τη συγχρονικότητα Shared-Nothing βάσει καναλιών – η οποία όντως κλιμακώνεται, αλλά από όσο γνωρίζω, (α) ΔΕΝ επιβάλλεται στο Go και (β) ο χρόνος εκτέλεσης του golang (σε αντίθεση, ας πούμε , Erlang/OTP Runtime) δεν υποστηρίζει/εντοπίζει «καθαρές» γορουτίνες χωρίς παγκόσμια πρόσβαση χωρίς const (οι οποίες θα μπορούσαν να μετακινηθούν ακόμη και σε άλλο πλαίσιο εάν είναι απαραίτητο). BTW, Node.js είναι Shared-Nothing by design – επιβολής το μόνο επεκτάσιμο μοντέλο εκεί έξω (και μας σώζει επίσης από ΠΟΛΛΑ σφάλματα που είναι σχεδόν αδύνατο να βρεθούν ως δευτερεύον πλεονέκτημα).
2 και η ανίχνευση αγώνα χρόνου εκτέλεσης του Go παρέχει μόνο μια παρηγορητική φροντίδα σε αυτό το πρόβλημα μεγάλου λίπους(tm)
Μύθος #4: Η ταυτόχρονη χρήση στο Golang είναι εύκολη
„συγχρονισμός (άσε ήσυχο αποτελεσματικός συγχρονισμός) δεν είναι ποτέ εύκολο[Cheney] λέει: «Η λειτουργία επικεφαλίδας του Go είναι το απλό, ελαφρύ μοντέλο συγχρονισμού μας. Ως προϊόν, η γλώσσα μας σχεδόν πουλάει τον εαυτό της μόνο σε αυτό το χαρακτηριστικό.“
Αλήθεια Νο 4: Golang ή όχι, συγχρονισμός (άσε ήσυχο αποτελεσματικός συγχρονισμός) δεν είναι ποτέ εύκολο. Για να το διευκρινίσουμε, ας ρίξουμε μια ματιά σε ένα εξαιρετικό άρθρο [Deleplace] σε μια εμπειρία βελτιστοποίησης με το Golang. Long story short – πρώτον, υποτιθέμενο «καλό»3 Κατασκευάστηκε ο κώδικας Golang 2,5 φορές πιο γρήγορα αφαιρώντας τη λεπτή συγχορήγηση και φτιάχνοντας το όλο θέμα ακολουθητικός(!!). Στη συνέχεια, κατά την προσθήκη α χοντρό σιτάρι ταυτόχρονα, ο χρόνος του ρολογιού τοίχου βελτιώθηκε περαιτέρω, αλλά ο αριθμός των γορουτίνων που πήγαιναν στα 120K αποδείχτηκε υπερβολικός για το χρόνο εκτέλεσης Go (προκαλώντας 12 πυρήνες να αποδίδουν μόνο 1,5 φορές καλύτερα από την έκδοση ενός πυρήνα – αυτό είναι περίπου 8 φορές αναποτελεσματικότητα όσον αφορά την CPU, ενέργεια -σύμφωνα με το CO2)4, επομένως η τελική εφαρμογή (η πιο αποτελεσματική, με εντυπωσιακή βελτίωση 23 φορές σε σχέση με τον αρχικό «καλό» κώδικα) βασίστηκε σε 12 εργαζόμενους. Αλλά hey – όχι μόνο αυτός ο αγώνας βελτιστοποίησης συγχρονισμού είναι πολύ παρόμοιος Ανεξάρτητα της γλώσσας προγραμματισμού, επιπλέον δεν μπορώ να μην αναφέρω ότι οι 12-εργάτες για 12-πύρηνες θα ήταν ο τρόπος με τον οποίο θα κλιμακώναμε την υλοποίηση του Node.js στην πρώτη θέση! Με άλλα λόγια –
Οι αποτελεσματικές ταυτόχρονες υλοποιήσεις τυχαίνει να είναι εντυπωσιακά παρόμοιες στο Golang και στο Node.js
Δεν υπάρχει καμία μαγεία εκεί έξω – και ενώ το κόστος των γορουτίνων είναι κάπως χαμηλότερο από αυτό των νημάτων, κοινές τεχνικές όπως η χονδρόκοκκη και ο περιορισμός του αριθμού των εκκρεμών εργασιών εξακολουθούν να είναι απαραίτητες.
3 τουλάχιστον κάποιος προγραμματιστής Golang δεν ντρεπόταν να το βάλει στο Github ως σαφές παράδειγμα ενός „καλού“ κώδικα
4 καταρρίπτοντας ουσιαστικά έναν ακόμη μύθο ότι το Golang είναι εντάξει εκατομμύρια από γορουτίνες
Μύθος #5: Η απόδοση του Golang είναι παρόμοια με αυτή του C/C++
[Sathananthan] λέει ότι το Golang έχει «Παρόμοια χαρακτηριστικά απόδοσης όπως με το C ή το C++».
Αλήθεια Νο 5: αν ρίξουμε μια ματιά στο IMO-the-best-inter-language-benchmark-results-available ([Debian1][Debian2]), θα δούμε ότι η Go έχασε και τα 10 σημεία αναφοράς στο GCC C, χάνοντας από 20% σε 5x (με διάμεσο περίπου 2,5x). Επιπλέον, αν μιλάμε για απόδοση του Node.js ([Debian3]), συμβαίνει να είναι μεταξύ 20% γρηγορότερα και 5,9x βραδύτερη από το Golang (με διάμεσο 1,4x). Με άλλα λόγια,
Από άποψη απόδοσης, το Golang είναι πιο κοντά στο Node.js παρά στο C/C++
Συμπέρασμα
„ΔΕΝ πιστεύω ότι είτε το Node.js είτε το Golang είναι «καλύτερο» από μόνο τουΠέρα από τον μύθο, ΔΕΝ πιστεύω ότι είτε το Node.js είτε το Golang είναι «καλύτερο» από μόνο του. Κατά μία έννοια, είναι εντυπωσιακά παρόμοια (ειδικά αν μιλάμε για το Go με κανάλια αντί για mutexes), επομένως όλες οι προσπάθειες να προωθηθεί ένα από αυτά ως «εγγενώς καλύτερο» είναι σχεδόν άσκοπες. Το μόνο πεδίο όπου το Golang έχει ρεαλιστικό πλεονέκτημα, είναι αυτό το πλεονέκτημα απόδοσης 1,4x, αλλά αυτό πρόκειται να αλλάξει με την εμφάνιση του Node.cpp, το οποίο θα επιτρέψει την απόδοση όπως η C++ για εκείνους τους κόμβους που το χρειάζονται.
Αναγνώριση
Κινούμενα σχέδια του Sergey Gordeev από Gordeev Animation GraphicsΠράγα.