Επιδόσεις Python: εγγενείς βρόχοι NumPy, επανάκληση και κόστος μετατροπής
Μια ελεγχόμενη αριθμητική δοκιμή μόνο για CPU ακολουθεί πανομοιότυπες τιμές μέσω βρόχων CPython, αριθμητικής NumPy, περιτυλίγματος επανάκλησης Python και μεταγλωττισμένου βρόχου Numba.
Μια ελεγχόμενη αριθμητική δοκιμή μόνο για CPU ακολουθεί πανομοιότυπες τιμές μέσω βρόχων CPython, αριθμητικής NumPy, περιτυλίγματος επανάκλησης Python και μεταγλωττισμένου βρόχου Numba. Το NumPy με έτοιμη είσοδο είχε μεγάλο προβάδισμα, ενώ η μετατροπή από και προς λίστες Python μείωσε αυτό το πλεονέκτημα σε σχεδόν ισοπαλία. Το επεισόδιο εξηγεί το όριο εκτέλεσης και πώς να μετρήσετε το αποτέλεσμα που χρειάζεται μια εφαρμογή.
Διαβάστε την γραπτή έκδοση (Αγγλικά) ↗
Τι καλύπτει αυτό το βίντεο
- Η CPython μεταγλωττίζει τον κώδικα σε bytecode. Η επαναλαμβανόμενη εργασία διερμηνέα/αντικειμένων και ένας πληκτρολογημένος εγγενής αριθμητικός βρόχος είναι διαφορετικές διατάξεις εκτέλεσης.
- Η αριθμητική NumPy μπορεί να παραδώσει πληκτρολογημένες παρτίδες σε μεταγλωττισμένες λειτουργίες. ο αριθμητικός βρόχος εξακολουθεί να υπάρχει και οι προβολές μπορεί να έχουν μη συνεχείς διατάξεις.
- Μετρήστε τη μετατροπή και την απαιτούμενη αναπαράσταση εξόδου. Σε αυτήν την εκτέλεση, η μετατροπή λιστών Python σε πίνακες και αντίστροφα εξάλειψε το μεγάλο προβάδισμα του έτοιμου πίνακα.
- Οι μικροί φόρτοι εργασίας μπορούν να ευνοήσουν το συνηθισμένο Python επειδή τα σταθερά κόστη κλήσης πίνακα έχουν σημασία. Η κατανόηση οκτώ τιμών κέρδισε αυτή τη μετρημένη σύγκριση.
- Το np.vectorize είναι ένα περιτύλιγμα ευκολίας που καλεί την Python για διαδοχικά στοιχεία. μια διεπαφή παρτίδας από μόνη της δεν καθιερώνει εγγενή αριθμητική εκτέλεση.
- Οι μεταγλωττισμένοι βρόχοι σύνταξης Python μπορούν επίσης να είναι γρήγοροι. Συμπεριλάβετε το κόστος ψυχρής μεταγλώττισης/ρύθμισης όταν έχουν σημασία για τον καλούντα.
- Η εγγενής εκτέλεση, το CPU SIMD, η πολυνημάτωση και τα backends GPU είναι ξεχωριστές ιδέες. Οι διαθέσιμες λειτουργίες CPU δεν εντοπίζουν τις οδηγίες που χρησιμοποιούνται σε μια συγκεκριμένη εκτέλεση.
Μεταφρασμένη μεταγραφή
Μεταφράστηκε από την πρωτότυπη αγγλική αφήγηση. Ο διαθέσιμος ήχος και οι υπότιτλοι ελέγχονται από το YouTube.
Γιατί η ίδια CPU μπορεί να τελειώσει νωρίτερα;
0:00 Πιστεύετε ότι η Python χρειάζεται μια νέα γλώσσα για να γίνει γρήγορη. Η ίδια CPU εκτέλεσε την παρτίδα μας πιο γρήγορα αφού μετακινήθηκε ο βρόχος. Πού πήγε η δουλειά; Πότε εξαφανίζεται η επιτάχυνση; Ποια συντόμευση φέρνει πίσω την Python; Αυτό είναι το The Daily Diff, κάτω από την κουκούλα. Προσέξτε αυτή τη συντόμευση. Η CPython είναι η τυπική υλοποίηση της Python.
0:17 Μεταγλωττίζει τον πηγαίο κώδικά σας σε bytecode, και μετά εκτελεί αυτές τις οδηγίες σε έναν διερμηνέα. Το NumPy είναι μια αριθμητική βιβλιοθήκη της οποίας οι πίνακες περιέχουν τιμές με έναν δηλωμένο τύπο δεδομένων. Η ενσωματωμένη αριθμητική του μπορεί να παραδώσει μια ολόκληρη παρτίδα σε μεταγλωττισμένες λειτουργίες. Πάρτε ένα εκατομμύριο τιμές κινητής υποδιαστολής, τετραγωνίστε την καθεμία, και μετά προσθέστε την αρχική τιμή. Κάθε έκδοση λαμβάνει πανομοιότυπες τιμές και παράγει αντίστοιχες απαντήσεις. Οι τιμές είναι αρκετά μικρές για να κρατήσουν τις διαφορές στρογγυλοποίησης εκτός του αγώνα.
0:40 Σε αυτή τη μηχανή, η κατανόηση χρειάστηκε περίπου τριάντα τρία χιλιοστά του δευτερολέπτου. Το NumPy χρειάστηκε λιγότερο από ένα. Και οι δύο έτρεξαν στην CPU. Αυτή είναι μια χρήσιμη απόδειξη για αυτό το φόρτο εργασίας, και ένας τρομερός επικήδειος για μια γλώσσα προγραμματισμού. Ξεκινήστε με τον συνηθισμένο βρόχο.
Ποια εργασία επαναλαμβάνει ένας βρόχος Python;
0:53 Η Python παίρνει την επόμενη τιμή, εκτελεί την αριθμητική και προσθέτει την απάντηση. Μετά το κάνει ξανά. Ο πολλαπλασιασμός είναι φθηνός. Η ρύθμιση του επόμενου πολλαπλασιασμού, και η διαχείριση των αντικειμένων Python γύρω του, προσθέτει εργασία κάθε φορά. Αυτή είναι η πραγματική αποσυναρμολόγηση της δοκιμαζόμενης συνάρτησης. Η επανάληψη, ο πολλαπλασιασμός και η πρόσθεση εμφανίζονται ως εντολές bytecode. Οι εκδόσεις και οι διερμηνείς της Python κάνουν διαφορετικές επιλογές.
1:14 Αυτοί οι χρόνοι ανήκουν στην έκδοση που εμφανίζεται στην οθόνη, η οποία είναι η Python τρία δέκα. Μια λίστα Python αποθηκεύει αναφορές σε αντικείμενα, έτσι διαφορετικά είδη πραγμάτων μπορούν να μοιράζονται μια λίστα. Η αριθμητική μας εργασία χρησιμοποιεί αριθμούς παντού. Πληρώνει για μια γενική διάταξη ενώ ζητά μια επαναλαμβανόμενη υπηρεσία. Φανταστείτε να παραγγέλνετε τον ίδιο καφέ ένα εκατομμύριο φορές και να συμπληρώνετε μια νέα φόρμα για κάθε φλιτζάνι. Η καφετιέρα είναι μια χαρά.
1:36 Η γραφειοκρατία έχει αναπτύξει τη δική της καριέρα.
Πού μετακινεί το NumPy τον βρόχο;
1:38 Το NumPy αλλάζει το συμβόλαιο. Αυτός ο πίνακας περιέχει τιμές κινητής υποδιαστολής σταθερού μεγέθους με έναν δηλωμένο τύπο. Ο πίνακάς μας είναι συνεχής, οπότε οι τιμές του βρίσκονται μαζί στη μνήμη. Οι προβολές μπορούν να έχουν άλλες διατάξεις. Η αριθμητική πράξη γνωρίζει τι είδους δεδομένα λαμβάνει. Η κλήση παραδίδει την παρτίδα σε μια εγγενή λειτουργία. Ένας βρόχος εξακολουθεί να επισκέπτεται τις τιμές μέσα σε μεταγλωττισμένο κώδικα. Μπορεί να επαναχρησιμοποιήσει τη γνώση του τύπου σε αυτή την εργασία.
2:00 Η Python παραμένει το άτομο που παραδίδει την εργασία. Ο Itamar Turner Trauring εξηγεί τη διάκριση στο Python Speed. Οι διεπαφές παρτίδας, οι εγγενείς υλοποιήσεις και οι εντολές φορέα επεξεργαστή είναι ξεχωριστά πράγματα. Το όνομα της συνάρτησης παρέχει πολύ λίγα στοιχεία.
Γρήγορο σημαίνει GPU ή πολλοί πυρήνες CPU;
2:13 Αυτό το αποτέλεσμα χρησιμοποίησε NumPy στην CPU. Ένα backend GPU όπως το CuPy επιλέγει ένα διαφορετικό μέρος για πίνακες και λειτουργίες. Η αγορά μιας GPU αφήνει αυτόν τον συνηθισμένο βρόχο Python να εκτελείται εκεί που ήδη εκτελούνταν. Οι εντολές φορέα CPU μπορούν να επεξεργαστούν πολλές τιμές μαζί. Το NumPy διαθέτει μηχανισμούς για την επιλογή υποστηριζόμενων πυρήνων. Ο χρόνος εκτέλεσής μας αναφέρει τις διαθέσιμες λειτουργίες, αλλά δεν εντοπίζει τις οδηγίες αυτής της έκφρασης. Το να αποδώσουμε ολόκληρο το μετρημένο κέρδος στις εντολές φορέα θα υπερεκτιμούσε τα στοιχεία.
2:40 Το καλάθι αγορών GPU μπορεί να παραμείνει κλειστό. Υπάρχει κάτι βαθιά ικανοποιητικό σε μια διόρθωση απόδοσης που ακυρώνει επίσης μια αγορά. Δυστυχώς, το σημείο αναφοράς συνεχίζεται.
Πότε εξαφανίζεται η εντυπωσιακή επιτάχυνση;
2:49 Με οκτώ τιμές, η κατανόηση κέρδισε. Η κλήση του μηχανισμού πίνακα κόστισε περισσότερο από τη μικροσκοπική δουλειά. Μεγαλύτεροι έτοιμοι πίνακες προηγήθηκαν. Χρησιμοποιήσαμε επαναλαμβανόμενες δοκιμές και μια θερμαινόμενη διαδικασία. Νεότερες εκδόσεις Python μπορούν να αλλάξουν το σημείο διασταύρωσης. Μετά αλλάξτε αυτό που χρειάζεται η εφαρμογή. Ξεκινήστε με μια λίστα Python και ολοκληρώστε με μια νέα λίστα Python. Το NumPy πρέπει να μετατρέψει την είσοδο σε πίνακα και να μετατρέψει την απάντηση πίσω σε αντικείμενα
3:11 Python. Αυτά τα βήματα ανήκουν στο λογαριασμό αν η εφαρμογή σας τα χρειάζεται. Σε ένα εκατομμύριο τιμές, αυτή η πλήρης διαδρομή χρειάστηκε περίπου είκοσι οκτώ χιλιοστά του δευτερολέπτου. Η κατανόηση χρειάστηκε επίσης περίπου είκοσι οκτώ. Το δραματικό μας προβάδισμα έγινε σχεδόν ισοπαλία. Το γρήγορο μέρος ήταν αληθινό. Το πλεονέκτημα εξαρτάται από το να αφήνουμε τα δεδομένα να παραμένουν στην αριθμητική τους μορφή.
Ποια όρια ανήκουν στο σημείο αναφοράς;
3:28 Ακολουθήστε τα δεδομένα. Εάν η επόμενη λειτουργία δέχεται έναν πίνακα, διατηρήστε τον πίνακα. Εάν μετατρέπετε επανειλημμένα μεταξύ λιστών και πινάκων, μετρήστε αυτές τις διασταυρώσεις. Το να σταματήσετε πριν από μια δαπανηρή παράδοση δίνει ακριβείς αριθμούς για τη λάθος ερώτηση. Ο πολλαπλασιασμός αυτού του πίνακα δημιουργεί ένα προσωρινό αποτέλεσμα. Η πρόσθεση των αρχικών τιμών δημιουργεί την έξοδο. Η εγγενής εκτέλεση εξακολουθεί να χρειάζεται μνήμη και να μετακινεί δεδομένα.
3:47 Ορισμένες λειτουργίες δέχονται ένα buffer εξόδου, μειώνοντας τις κατανομές όταν ο κώδικάς σας μπορεί να το επαναχρησιμοποιήσει με ασφάλεια. Μια όμορφη γραμμή μπορεί να παραγγείλει πολλά μεγάλα buffers. Το Python Speed αναφέρει επίσης αυτό το πρόβλημα. Ο διαχειριστής μνήμης είναι εκπληκτικά αδιάφορος στην αισθητική σας.
Μπορεί ένας μεταγλωττισμένος βρόχος Python να είναι επίσης γρήγορος;
4:00 Μπορείτε επίσης να διατηρήσετε τη σύνταξη του βρόχου και να αλλάξετε την εκτέλεσή του. Το Numba μπορεί να μεταγλωττίσει υποστηριζόμενες αριθμητικές συναρτήσεις Python σε εγγενή κώδικα. Εδώ είναι ο βρόχος από τη δοκιμή μας με τον διακοσμητή μεταγλωττιστή του. Εξακολουθεί να λέει for. Η ακριβή διαδρομή του διερμηνέα έχει αλλάξει. Μετά τη μεταγλώττιση, αυτός ο βρόχος χρειάστηκε λιγότερο από μισό χιλιοστό του δευτερολέπτου με έτοιμους πίνακες. Η ίδια η μεταγλώττιση χρειάστηκε περίπου ένα δευτερόλεπτο σε αυτή την εκτέλεση. Αυτό το κόστος ρύθμισης εξαιρέθηκε από τις επαναλαμβανόμενες χρονομετρήσεις,
4:22 οπότε μια μικροσκοπική εφάπαξ εργασία αξίζει μια διαφορετική σύγκριση. Ο οδηγός του Numba είναι πρόθυμος να δείξει ρητούς βρόχους. Η έναρξη μιας μετάβασης γλώσσας πριν βρεθεί η hot path είναι ένας περίπλοκος τρόπος για να αποφύγετε το άνοιγμα ενός προφίλ. Πρώτα βρείτε τι καταναλώνει τον χρόνο. Οι αναμονές δικτύου, η ανάλυση αντικειμένων και η μαζική αριθμητική απαιτούν διαφορετικές διορθώσεις. Οι εγγενείς λειτουργίες πίνακα βοηθούν όταν ο υπολογισμός και τα δεδομένα ταιριάζουν.
4:43 Μετρήστε το αποτέλεσμα που χρειάζεται πραγματικά ο καλούντας σας. Τώρα για αυτή τη συντόμευση. Έχετε μια συνάρτηση Python και θέλετε να δέχεται έναν πίνακα. Ένας wrapper με το όνομα vectorize ακούγεται σαν το κουμπί απόδοσης που λείπει. Η τεκμηρίωσή του έχει μια σημαντική πρόταση που περιμένει.
Τι εκτελεί πραγματικά το np.vectorize;
4:56 Το NumPy λέει ότι ο wrapper vectorize παρέχεται κυρίως για ευκολία. Η υλοποίησή του είναι ουσιαστικά ένας βρόχος for. Ο wrapper δέχεται παρτίδες, αλλά συνεχίζει να καλεί τη συνάρτηση Python σας για διαδοχικά στοιχεία. Η διεπαφή έγινε μικρότερη. Το όριο εκτέλεσης παρέμεινε απασχολημένο. Παρακολουθήστε τις ίδιες τιμές να επιστρέφουν μέσω της επανάκλησης Python. Με έτοιμους πίνακες, αυτή η έκδοση χρειάστηκε περίπου ογδόντα επτά χιλιοστά του δευτερολέπτου. Ήταν πιο αργή από την κατανόησή μας.
5:18 Ένας χρήσιμος wrapper εκπομπής μπορεί να διατηρήσει την ακριβή εργασία ανά στοιχείο που προσπαθούσατε να αφαιρέσετε. Ελέγξτε την υλοποίηση πίσω από μια διεπαφή παρτίδας, διατηρήστε συμβατά δεδομένα στην χρήσιμη μορφή τους, και συμπεριλάβετε τη μετατροπή στη σύγκριση. Οι εγγενείς, οι επανάκλησης και οι μεταγλωττισμένοι βρόχοι μπορούν να φαίνονται παρόμοιοι από τη θέση του καλούντος. Το όνομα vectorize έκανε μια υπόσχεση που το χρονόμετρό σας δεν υπέγραψε ποτέ. Κρατήστε το όταν η ευκολία είναι αυτό που χρειάζεστε. Για απόδοση, ελέγξτε τον βρόχο που εκτελείται.
Τι πρέπει να αλλάξετε στον κώδικά σας;
5:41 Η ετυμηγορία μου είναι SHIP IT για τη δέσμευση μιας μετρημένης αριθμητικής hot path. Διατηρήστε τους συνηθισμένους βρόχους όταν ήδη λειτουργούν καλά. Ελέγξτε πού γίνεται η εργασία και τι χρειάζεται ο επόμενος καλούντας. Εγγραφείτε, πατήστε το κουδουνάκι και πείτε μου στα σχόλια αν θα το είχατε σφραγίσει διαφορετικά. Και αυτή είναι η διαφορά για σήμερα. Είμαι ο Νίκος από την Axrisi. Συγχωνεύστε υπεύθυνα.
Πηγές
- Universal functions (ufunc) basics — NumPy 2.2NumPy
- The N-dimensional array — NumPy 2.2NumPy
- numpy.vectorize — convenience and performance notesNumPy
- CPU/SIMD optimizations — NumPy 2.2NumPy
- dis — Disassembler for Python bytecodePython Software Foundation
- CPython 3.10.12 list representationCPython contributors
- The limits of Python vectorization as a performance techniqueItamar Turner-Trauring / Python⇒Speed
- Performance TipsNumba
- Basics of CuPyCuPy



