Προγραμματισμός

Multicore Python: Ένας σκληρός, άξιος και προσιτός στόχος

Για όλες τις εξαιρετικές και βολικές δυνατότητες της Python, ένας στόχος παραμένει απρόσιτος: Οι εφαρμογές Python εκτελούνται στον διερμηνέα αναφοράς CPython και χρησιμοποιούν πολλούς πυρήνες CPU παράλληλα.

Αυτό ήταν από καιρό ένα από τα μεγαλύτερα εμπόδια της Python, ειδικά επειδή όλες οι λύσεις είναι αδέξιες. Η επείγουσα ανάγκη για εξεύρεση μακροπρόθεσμης λύσης στο ζήτημα αυξάνεται, ιδίως καθώς ο βασικός υπολογισμός των επεξεργαστών συνεχίζει να αυξάνεται (βλ. 24-πυρήνα της Intel).

Μία κλειδαριά για όλους

Στην πραγματικότητα, είναι δυνατό να χρησιμοποιήσετε νήματα σε εφαρμογές Python - πολλά από αυτά έχουν ήδη. Τι είναιδεν Είναι πιθανό το CPython να εκτελεί εφαρμογές πολλαπλών νημάτων με κάθε νήμα να εκτελείται παράλληλα σε διαφορετικό πυρήνα. Η διαχείριση της εσωτερικής μνήμης του CPython δεν είναι ασφαλής για το νήμα, οπότε ο διερμηνέας εκτελεί μόνο ένα νήμα τη φορά, αλλάζοντας μεταξύ τους ανάλογα με τις ανάγκες και ελέγχοντας την πρόσβαση στην παγκόσμια κατάσταση.

Αυτός ο μηχανισμός κλειδώματος, το Global Interpreter Lock (GIL), είναι ο μοναδικός μεγαλύτερος λόγος για τον οποίο το CPython δεν μπορεί να εκτελέσει παράλληλα νήματα. Υπάρχουν μερικοί ελαφρυντικοί παράγοντες. Για παράδειγμα, οι λειτουργίες εισόδου / εξόδου, όπως οι δίσκοι ή οι αναγνώσεις δικτύου δεν δεσμεύονται από το GIL, επομένως αυτές μπορούν να εκτελούνται ελεύθερα στα δικά τους νήματα. Όμως, οτιδήποτε τόσο πολυνηματικό όσο και συνδεδεμένο με CPU είναι πρόβλημα.

Για τους προγραμματιστές Python, αυτό σημαίνει ότι οι βαριές υπολογιστικές εργασίες που επωφελούνται από τη διάδοση σε πολλούς πυρήνες δεν λειτουργούν καλά, εμποδίζοντας τη χρήση μιας εξωτερικής βιβλιοθήκης. Η ευκολία εργασίας στην Python έρχεται με ένα σημαντικό κόστος απόδοσης, το οποίο γίνεται πιο δύσκολο να καταπιεί όσο πιο γρήγορα, εξίσου βολικές γλώσσες όπως το Google's Go έρχονται στο προσκήνιο.

Διαλέξτε την κλειδαριά

Με την πάροδο του χρόνου, εμφανίστηκαν πολλές επιλογές που βελτιώνουν - αλλά δεν εξαλείφουν - τα όρια του GIL. Μια τυπική τακτική είναι να ξεκινήσετε πολλές παρουσίες του CPython και να μοιραστείτε το περιβάλλον και την κατάσταση μεταξύ τους. κάθε παρουσία εκτελείται ανεξάρτητα από την άλλη σε ξεχωριστή διαδικασία. Όμως, όπως εξηγεί ο Jeff Knupp, τα κέρδη που παρέχονται από το τρέξιμο παράλληλα μπορεί να χαθούν από την προσπάθεια που απαιτείται για την κοινή χρήση της κατάστασης, οπότε αυτή η τεχνική ταιριάζει καλύτερα σε μακροχρόνιες λειτουργίες που συγκεντρώνουν τα αποτελέσματά τους με την πάροδο του χρόνου.

Οι επεκτάσεις C δεν δεσμεύονται από το GIL, τόσες πολλές βιβλιοθήκες για την Python που χρειάζονται ταχύτητα (όπως η βιβλιοθήκη μαθηματικών και στατιστικών Numpy) μπορούν να εκτελεστούν σε πολλούς πυρήνες. Όμως οι περιορισμοί στο ίδιο το CPython παραμένουν. Εάν ο καλύτερος τρόπος για να αποφύγετε το GIL είναι να χρησιμοποιήσετε το C, αυτό θα οδηγήσει περισσότερους προγραμματιστές μακριά από την Python και προς το C.

Το PyPy, η έκδοση Python που μεταγλωττίζει κώδικα μέσω JIT, δεν ξεφορτώνεται το GIL, αλλά το αντισταθμίζει απλώς με τον κώδικα να τρέχει πιο γρήγορα. Με κάποιους τρόπους, αυτό δεν είναι κακό υποκατάστατο: Εάν η ταχύτητα είναι ο κύριος λόγος για τον οποίο παρακολουθείτε το multithreading, το PyPy μπορεί να είναι σε θέση να παρέχει την ταχύτητα χωρίς τις επιπλοκές του multithreading.

Τέλος, το ίδιο το GIL επανεπεξεργάστηκε κάπως στο Python 3, με έναν καλύτερο χειριστή εναλλαγής νήματος. Όμως όλες οι υποκείμενες υποθέσεις - και οι περιορισμοί - παραμένουν. Υπάρχει ακόμα ένα GIL και συνεχίζει τις διαδικασίες.

Όχι GIL; Κανένα πρόβλημα

Παρ 'όλα αυτά, η αναζήτηση για Python χωρίς GIL, συμβατή με υπάρχουσες εφαρμογές, συνεχίζεται. Άλλες εφαρμογές του Python έχουν καταργήσει εντελώς το GIL, αλλά με κόστος. Το Jython, για παράδειγμα, τρέχει πάνω από το JVM και χρησιμοποιεί το σύστημα παρακολούθησης αντικειμένων του JVM αντί για το GIL. Η IronPython ακολουθεί την ίδια προσέγγιση μέσω του CLR της Microsoft. Αλλά και οι δύο πάσχουν από ασυνεπή απόδοση και μερικές φορές τρέχουν πολύ πιο αργά από το CPython. Επίσης, δεν μπορούν να διασυνδεθούν εύκολα με εξωτερικό κώδικα C, έτσι πολλές υπάρχουσες εφαρμογές Python δεν θα λειτουργήσουν.

Το PyParallel, ένα έργο που δημιουργήθηκε από τον Trent Nelson της Continuum Analytics, είναι ένα "πειραματικό, δοκιμαστικό πιρούνι Python 3 που έχει σχεδιαστεί για τη βέλτιστη εκμετάλλευση πολλαπλών πυρήνων CPU." Δεν αφαιρεί το GIL, αλλά βελτιώνει τον αντίκτυπό του αντικαθιστώντας το ασύγχρονος ενότητα, έτσι εφαρμογές που χρησιμοποιούνασύγχρονος για τον παραλληλισμό (όπως το I / O πολλαπλών νημάτων όπως ένας διακομιστής Ιστού) επωφελούνται περισσότερο. Το έργο ήταν αδρανές για αρκετούς μήνες, αλλά η τεκμηρίωσή του αναφέρει ότι οι προγραμματιστές του είναι άνετοι να πάρουν το χρόνο τους για να το κάνουν σωστό, έτσι μπορεί τελικά να συμπεριληφθεί στο CPython: "Δεν υπάρχει τίποτα λάθος με αργό και σταθερό όσο κατευθύνεστε προς τη σωστή κατεύθυνση. "

Ένα μακροχρόνιο έργο από τους δημιουργούς της PyPy ήταν η έκδοση του Python που χρησιμοποιεί μια τεχνική που ονομάζεται "λογισμικό συναλλαγής μνήμης" (PyPy-STM). Το πλεονέκτημα, σύμφωνα με τους δημιουργούς της PyPy, είναι "μπορείτε να κάνετε μικρές τροποποιήσεις στα υπάρχοντα, μη πολυνηματικά προγράμματα σας και να τα κάνετε να χρησιμοποιούν πολλούς πυρήνες."

Το PyPy-STM ακούγεται σαν μαγικό, αλλά έχει δύο μειονεκτήματα. Πρώτον, είναι ένα έργο σε εξέλιξη που προς το παρόν υποστηρίζει μόνο το Python 2.x και δεύτερον, εξακολουθεί να έχει επιτυχία για εφαρμογές που εκτελούνται σε έναν μόνο πυρήνα. Δεδομένου ότι μία από τις προϋποθέσεις που ανέφερε ο δημιουργός της Python Guido van Rossum για οποιεσδήποτε απόπειρες αφαίρεσης του GIL από το CPython είναι ότι η αντικατάστασή του δεν πρέπει να υποβαθμίζει την απόδοση για εφαρμογές ενός πυρήνα με μονό σπείρωμα, μια τέτοια λύση δεν θα προσγειωθεί στο CPython στην τρέχουσα κατάσταση.

Βιάσου και περίμενε

Ο Larry Hastings, ένας βασικός προγραμματιστής της Python, μοιράστηκε μερικές από τις απόψεις του στο PyCon 2016 σχετικά με το πώς θα μπορούσε να αφαιρεθεί το GIL. Ο Χέιστινγκς τεκμηρίωσε τις προσπάθειές του να αφαιρέσει το GIL και με αυτόν τον τρόπο κατέληξε σε μια έκδοση του Python που δεν είχε GIL, αλλά έτρεξε με αγωνία αργά εξαιτίας συνεχών ελλείψεων cache.

Μπορείτε να χάσετε το GIL, συνοψίζοντας τον Hastings, αλλά πρέπει να έχετε κάποιο τρόπο να εγγυηθείτε ότι μόνο ένα νήμα τη φορά τροποποιεί τα παγκόσμια αντικείμενα - για παράδειγμα, έχοντας ένα ειδικό νήμα στον διερμηνέα να χειριστεί τέτοιες αλλαγές κατάστασης.

Ένα κομμάτι μακροπρόθεσμων καλών ειδήσεων είναι ότι εάν και όταν το CPython ρίξει το GIL, οι προγραμματιστές που χρησιμοποιούν τη γλώσσα θα είναι ήδη έτοιμοι να εκμεταλλευτούν το multithreading Πολλές αλλαγές έγιναν τώρα στη σύνταξη του Python, όπως οι ουρές και το ασύγχρονος/αναμένω λέξεις-κλειδιά για το Python 3.5, διευκολύνουν την κατανομή εργασιών μεταξύ πυρήνων σε υψηλό επίπεδο.

Ακόμα, ο απαιτούμενος όγκος εργασίας για να κάνει το Python GIL λιγότερο, αλλά εγγυάται ότι θα εμφανιστεί πρώτα σε μια ξεχωριστή εφαρμογή όπως το PyPy-STM. Όσοι θέλουν να δοκιμάσουν ένα σύστημα χωρίς GIL μπορούν να το κάνουν μέσω μιας τέτοιας προσπάθειας τρίτου μέρους, αλλά το αρχικό CPython είναι πιθανό να παραμείνει ανέγγιχτο προς το παρόν. Εδώ ελπίζω ότι η αναμονή δεν είναι πολύ μεγαλύτερη.