Αυτό το άρθρο είναι ένα άρθρο καθρέφτη της αυτόματης μετάφρασης, κάντε κλικ εδώ για να μεταβείτε στο αρχικό άρθρο.

Άποψη: 6317|Απάντηση: 3

[Πηγή] [Στροφή]. Βελτιστοποιήσεις απόδοσης NET - διασχίστε γρήγορα τις συλλογές λιστών

[Αντιγραφή συνδέσμου]
Δημοσιεύτηκε στις 2022-8-28 20:51:16 | | | |
Σύντομη εισαγωγή

Το System.Collections.Generic.List <T>είναι μια γενική κλάση συλλογής στο .NET, η οποία μπορεί να αποθηκεύσει οποιονδήποτε τύπο δεδομένων λόγω της ευκολίας και του πλούσιου API της, η οποία χρησιμοποιείται ευρέως στην καθημερινή μας ζωή και μπορούμε να πούμε ότι είναι η πιο χρησιμοποιούμενη κατηγορία συλλογής.

Στη σύνταξη κώδικα, συχνά χρειάζεται να επαναλάβουμε μια <T>συλλογή λίστας για να αποκτήσουμε τα στοιχεία σε αυτήν για κάποια επιχειρηματική επεξεργασία. Κανονικά, δεν υπάρχουν πολλά στοιχεία μέσα σε ένα σετ και είναι πολύ γρήγορο να το διασχίσεις. Αλλά για κάποια επεξεργασία μεγάλων δεδομένων, στατιστικές, υπολογιστές σε πραγματικό χρόνο κ.λπ.<T>, πώς να διασχίσετε γρήγορα τη συλλογή λίστας δεκάδων χιλιάδων ή εκατοντάδων χιλιάδων δεδομένων; Αυτό πρέπει να μοιραστώ μαζί σας σήμερα.

Λειτουργία διέλευσης

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

Χρήση της δήλωσης foreach

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

Επειδή η δήλωση foreach είναι μια σύνταξη sugar, ο μεταγλωττιστής καλεί τελικά τις GetEnumerator() και MoveNext() με έναν βρόχο while για να υλοποιήσει τη λειτουργικότητα. Ο μεταγλωττισμένος κώδικας μοιάζει με αυτό:



Η υλοποίηση της μεθόδου MoveNext() θα διασφαλίσει ότι δεν θα υπάρχουν άλλα νήματα που να τροποποιούν τη συλλογή στην επανάληψη και εάν συμβεί η τροποποίηση, θα δημιουργήσει μια εξαίρεση InvalidOperationException και θα έχει έλεγχο υπερχείλισης για να ελέγξει εάν το τρέχον ευρετήριο είναι νόμιμο και πρέπει επίσης να εκχωρήσει το αντίστοιχο στοιχείο στον απαριθμητή. Τρέχον χαρακτηριστικό,Άρα στην πραγματικότητα, η απόδοσή του δεν είναι και η καλύτερη, το απόσπασμα κώδικα μοιάζει με αυτό:



Ας ρίξουμε μια ματιά στην απόδοσή του σε διαφορετικά μεγέθη σετ και τα αποτελέσματα μοιάζουν με αυτό:



Μπορεί να φανεί ότι στην περίπτωση διαφορετικών μεγεθών, απαιτείται η γραμμική σχέση ανάπτυξης της χρονοβόρας διαδικασίας, ακόμα κι αν διασχίζει 100w δεδομένων χωρίς καμία λογική επεξεργασίας, χρειάζεται τουλάχιστον 1 δευτερόλεπτο.

Χρησιμοποιήστε τη μέθοδο ForEach της λίστας

Ένας άλλος συνηθισμένος τρόπος είναι να χρησιμοποιήσετε το List<T>. ForEach(), η οποία σας επιτρέπει να μεταβιβάσετε έναν <T>πληρεξούσιο ενέργειας, ο οποίος θα καλεί τον πληρεξούσιο ενέργειας καθώς επαναλαμβάνεται στο στοιχείο<T>.

Είναι μια <T>εσωτερική μέθοδος υλοποίησης του List, ώστε να μπορεί να έχει άμεση πρόσβαση σε ιδιωτικούς πίνακες και να αποφεύγει τους ελέγχους υπερχείλισης. Θεωρητικά, θα πρέπει να είναι γρήγορο. Αλλά στο σενάριό μας υπάρχει μόνο μία κενή μέθοδος, η οποία μπορεί να μην συμπεριφέρεται καλά με μια πλήρως ενσωματωμένη κλήση στη μέθοδο foreach. Παρακάτω είναι ο πηγαίος κώδικας της μεθόδου ForEach, ο οποίος δείχνει ότι δεν διαθέτει έλεγχο υπερχείλισης, αλλά εξακολουθεί να διατηρεί τον ταυτόχρονο έλεγχο αριθμού έκδοσης.



Επιπλέον, δεδομένου ότι είναι απαραίτητο να περάσετε έναν εκπρόσωπο στη μέθοδο ForEach, στον κωδικό κλήσης, θα ελέγχει εάν το αντικείμενο αντιπροσώπου στην κλάση δημιουργίας κλεισίματος είναι κενό κάθε φορά, και αν όχι, νέο Action<T>(), όπως φαίνεται παρακάτω:



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



Κρίνοντας από τα αποτελέσματα των δοκιμών, είναι 40% πιο αργό από την απευθείας χρήση της λέξης-κλειδιού foreach, φαίνεται ότι εάν δεν είναι απαραίτητο, είναι καλύτερη επιλογή να χρησιμοποιήσετε απευθείας το foreach, οπότε υπάρχει πιο γρήγορος τρόπος;

για διέλευση βρόχου

Επιστρέφοντας στον παλαιότερο τρόπο μας, που είναι να χρησιμοποιήσουμε τη λέξη-κλειδί for για να διασχίσουμε τη συλλογή. Θα πρέπει να είναι η μέθοδος διέλευσης με την καλύτερη απόδοση αυτή τη στιγμή, επειδή δεν απαιτεί κάποιο περιττό κώδικα όπως οι προηγούμενες (αν και το ευρετήριο ελέγχεται επίσης για την αποφυγή υπερχείλισης) και προφανώς δεν ελέγχει τον αριθμό έκδοσης, επομένως σε ένα περιβάλλον πολλαπλών νημάτων η συλλογή αλλάζει και δεν θα υπάρχει εξαίρεση κατά τη χρήση για. Ο κωδικός δοκιμής μοιάζει με αυτό:

Ας δούμε πώς θα εξελιχθεί.



Αυτός φαίνεται να είναι ο τρόπος που το περιμένουμε.Η απευθείας χρήση του βρόχου for είναι 60% ταχύτερη από το foreach, ένα σετ που χρειαζόταν 1 δευτερόλεπτο για να διασχίσει, τώρα χρειάζεται μόνο 400 χιλιοστά του δευτερολέπτου. Υπάρχει λοιπόν πιο γρήγορος τρόπος;

Χρησιμοποιήστε τις ΣυλλογέςMarshal

Μετά το .NET5, η κοινότητα dotnet εφάρμοσε την κλάση CollectionsMarshal προκειμένου να βελτιώσει την απόδοση των λειτουργιών συλλογής. Αυτή η κλάση υλοποιεί τον τρόπο πρόσβασης σε εγγενείς πίνακες τύπων συλλογών (αν έχετε δει το [. .NET Performance Optimization - You Should Set the Initial Size for Collection Types], γνωρίζετε ότι η υποκείμενη υλοποίηση πολλών δομών δεδομένων είναι οι πίνακες). Έτσι, μπορεί να παρακάμψει όλα τα είδη ανιχνεύσεων και να έχει άμεση πρόσβαση στην αρχική συστοιχία, η οποία θα πρέπει να είναι η ταχύτερη. Ο κώδικας μοιάζει με αυτό:

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



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



Πω πωΗ χρήση του CollectionsMarshal είναι 79% ταχύτερη από τη χρήση του foreach, αλλά θα πρέπει να είναι ο λόγος για τη βελτιστοποίηση JIT, δεν υπάρχει μεγάλη διαφορά μεταξύ της χρήσης του foreach και του βρόχου λέξεων-κλειδιών Span.

περίληψη

Σήμερα σας μίλησα για το πώς να διασχίσετε γρήγορα τη συλλογή List και στις περισσότερες περιπτώσεις συνιστάται η χρήση της λέξης-κλειδιού foreach, η οποία διαθέτει έλεγχο υπερχείλισης και έλεγχο αριθμού έκδοσης πολλαπλών νημάτων, που μπορεί να μας διευκολύνει να γράψουμε τον σωστό κώδικα.

Εάν χρειάζεστε υψηλή απόδοση και μεγάλους όγκους δεδομένων, συνιστάται να χρησιμοποιήσετε το for και το CollectionsMarshal.AsSpan απευθείας για να διασχίσετε τη συλλογή.

Σύνδεσμος πηγαίου κώδικα αυτού του άρθρου:

Η σύνδεση με υπερσύνδεσμο είναι ορατή.

Αρχικός σύνδεσμος:Η σύνδεση με υπερσύνδεσμο είναι ορατή.





Προηγούμενος:Λεπτομερής επεξήγηση της αρχιτεκτονικής μηνυμάτων RabbitMQ AMQP
Επόμενος:Καλώδιο δικτύου κρυστάλλινη κεφαλή T568A και T568B πρότυπο και διαφορά
Δημοσιεύτηκε στις 2022-9-4 22:15:52 |
Μαθαίνω να μαθαίνω
Δημοσιεύτηκε στις 2022-9-8 10:33:05 |
Μαθαίνω να μαθαίνω
Δημοσιεύτηκε στις 2023-6-27 22:39:13 |
Γεια σας 12306 Μπορείτε να μου στείλετε ένα προσωπικό μήνυμα με δεδομένα
Αποκήρυξη:
Όλο το λογισμικό, το υλικό προγραμματισμού ή τα άρθρα που δημοσιεύονται από το Code Farmer Network προορίζονται μόνο για μαθησιακούς και ερευνητικούς σκοπούς. Το παραπάνω περιεχόμενο δεν θα χρησιμοποιηθεί για εμπορικούς ή παράνομους σκοπούς, άλλως οι χρήστες θα υποστούν όλες τις συνέπειες. Οι πληροφορίες σε αυτόν τον ιστότοπο προέρχονται από το Διαδίκτυο και οι διαφορές πνευματικών δικαιωμάτων δεν έχουν καμία σχέση με αυτόν τον ιστότοπο. Πρέπει να διαγράψετε εντελώς το παραπάνω περιεχόμενο από τον υπολογιστή σας εντός 24 ωρών από τη λήψη. Εάν σας αρέσει το πρόγραμμα, υποστηρίξτε γνήσιο λογισμικό, αγοράστε εγγραφή και λάβετε καλύτερες γνήσιες υπηρεσίες. Εάν υπάρχει οποιαδήποτε παραβίαση, επικοινωνήστε μαζί μας μέσω email.

Mail To:help@itsvse.com