Σύντομη εισαγωγή
Η βελτιστοποίηση απόδοσης είναι ο τρόπος με τον οποίο διασφαλίζεται ότι ο ίδιος αριθμός αιτημάτων υποβάλλεται σε επεξεργασία με λιγότερους πόρους, οι οποίοι είναι γενικά CPU ή μνήμη, και φυσικά, χειρισμοί IO λειτουργικού συστήματος, κίνηση δικτύου, χρήση δίσκου κ.λπ. Αλλά τις περισσότερες φορές, μειώνουμε τη χρήση της CPU και της μνήμης. Το περιεχόμενο που κοινοποιήθηκε πριν έχει ορισμένους περιορισμούς, είναι δύσκολο να μεταμορφωθεί άμεσα, σήμερα θέλω να μοιραστώ μαζί σας μια απλή μέθοδο, χρειάζεται μόνο να αντικαταστήσετε μερικούς τύπους συλλογής, για να επιτύχετε το αποτέλεσμα της βελτίωσης της απόδοσης και της μείωσης του αποτυπώματος μνήμης. Σήμερα θέλω να μοιραστώ μαζί σας μια βιβλιοθήκη τάξης, αυτόΗ βιβλιοθήκη κλάσεων ονομάζεται Collections.Pooled, Όπως φαίνεται από το όνομα, είναι μέσω της συγκεντρωτικής μνήμης για να επιτευχθεί ο σκοπός της μείωσης του αποτυπώματος μνήμης και του GC, και θα δούμε άμεσα πώς είναι η απόδοσή του και θα σας μεταφέρουμε επίσης να δείτε τον πηγαίο κώδικα, γιατί φέρνει αυτές τις βελτιώσεις απόδοσης.
Συλλογές.Συγκεντρωτικές
Σύνδεσμος έργου:Η σύνδεση με υπερσύνδεσμο είναι ορατή.
Η βιβλιοθήκη βασίζεται σε στο System.Collections.Generic, οι οποίες έχουν τροποποιηθεί για να επωφεληθούν από τις νέες <T>βιβλιοθήκες κλάσεων System.Span και System.Buffers.ArrayPool <T>με σκοπό τη μείωση της εκχώρησης μνήμης, τη βελτίωση της απόδοσης και τη μεγαλύτερη διαλειτουργικότητα με τα σύγχρονα API. Το Collections.Pooled υποστηρίζει .NET Standnd 2.0 (.NET Framework 4.6.1+), καθώς και υποστήριξη για . NET Core 2.1+. Ένα εκτεταμένο σύνολο δοκιμών μονάδων και σημείων αναφοράς έχει μεταφερθεί από το CoreFX.
Συνολικός αριθμός τεστ: 27501. Μέσω: 27501. Αποτυχία: 0. Παράλειψη: 0. Η δοκιμαστική λειτουργία ήταν επιτυχής. Χρόνος εκτέλεσης δοκιμής: 9,9019 δευτερόλεπτα
Τρόπος χρήσης
Μπορείτε εύκολα να εγκαταστήσετε αυτήν τη βιβλιοθήκη μέσω του Nuget, NuGet Version.
Στη βιβλιοθήκη Collections.Pooled, υλοποιεί συγκεντρωτικές εκδόσεις για τους τύπους συλλογών που χρησιμοποιούμε συνήθως, όπως φαίνεται στη σύγκριση με τους εγγενείς τύπους .NET.
| . .NET εγγενές | Συλλογές.Συγκεντρωτικές | παρατήρηση | | Λίστα<T> | ΣυγκεντρωτικήΛίστα<T> | Γενικές συλλογής | | Λεξικό<TKey, TValue> | Συγκεντρωτικό Λεξικό<TKey, TValue> | Γενική κλάση λεξικού | | Σύνολο κατακερματισμού<T> | PooledSet<T> | Γενικές συλλογής κατακερματισμών | | Στοίβα<T> | Στοίβα<T> | Γενικές στοίβες | | Ουρά<T> | PooledQueue<T> | Γενική κοόρτη |
Κατά τη χρήση, χρειάζεται μόνο να προσθέσουμε το αντίστοιχο . .NET με την έκδοση Collections.Pooled, όπως φαίνεται στον παρακάτω κώδικα:
Ωστόσο, πρέπει να σημειώσουμε ότι ο τύπος Pooled υλοποιεί τη διεπαφή IDispose, η οποία επιστρέφει τη χρησιμοποιημένη μνήμη στο pool μέσω της μεθόδου Dispose(), επομένως πρέπει να καλέσουμε τη μέθοδο Dispose() μετά τη χρήση του αντικειμένου συλλογής Pooled. Ή μπορείτε να χρησιμοποιήσετε απευθείας τη λέξη-κλειδί var.
Σημείωση: Χρησιμοποιήστε το αντικείμενο συλλογής μέσα στο Collections.PooledΕίναι καλύτερο να χρειαστεί να το απελευθερώσετε χειροκίνητα, αλλά δεν έχει σημασία αν δεν το απελευθερώσετε, το GC θα το ανακυκλώσει τελικά, αλλά δεν μπορεί να επιστραφεί στην πισίνα και δεν θα επιτύχει το αποτέλεσμα της εξοικονόμησης μνήμης. Δεδομένου ότι επαναχρησιμοποιεί χώρο μνήμης, όταν επιστρέφει χώρο μνήμης στο χώρο συγκέντρωσης, πρέπει να επεξεργαστεί τα στοιχεία της συλλογής και παρέχει μια απαρίθμηση που ονομάζεται ClearMode για χρήση, η οποία ορίζεται ως εξής:
Από προεπιλογή, μπορείτε να χρησιμοποιήσετε την προεπιλεγμένη τιμή Αυτόματο και, εάν υπάρχουν ειδικές απαιτήσεις απόδοσης, μπορείτε να χρησιμοποιήσετε την επιλογή Ποτέ αφού γνωρίζετε τους κινδύνους. Για τύπους αναφοράς και τύπους τιμών που περιέχουν τύπους αναφοράς, πρέπει να αδειάσουμε την αναφορά πίνακα κατά την επιστροφή του χώρου μνήμης στην πισίνα, εάν δεν διαγραφεί, το GC δεν θα μπορεί να ελευθερώσει αυτό το τμήμα του χώρου μνήμης (επειδή η αναφορά του στοιχείου κρατούσε πάντα η πισίνα), εάν είναι καθαρός τύπος τιμής, τότε δεν μπορεί να αδειάσει, σε αυτό το άρθρο περιγράφω τη διαφορά αποθήκευσης μεταξύ των τύπων αναφοράς και των πινάκων struct (τύπος τιμής), οι τύποι καθαρών τιμών δεν έχουν ανακύκλωση κεφαλίδας αντικειμένου και δεν απαιτούν παρέμβαση GC.
. .NET Performance Optimization - Χρησιμοποιήστε εναλλακτικές struct:Η σύνδεση με υπερσύνδεσμο είναι ορατή.
Σύγκριση απόδοσης
Δεν έκανα το Benchmark μόνος μου και τα αποτελέσματα της βαθμολογίας τρεξίματος των έργων ανοιχτού κώδικα που χρησιμοποίησα απευθείας ήταν 0 για τη χρήση μνήμης πολλών έργων, κάτι που συνέβη επειδή η συγκεντρωτική μνήμη που χρησιμοποιήθηκε δεν είχε επιπλέον κατανομή.
ΣυγκεντρωτικήΛίστα<T>
Κάντε βρόχο στα στοιχεία του 2048 που προστέθηκαν στο σύνολο στο Benchmark, . Η εγγενής λίστα .NET <T>απαιτεί 110us (σύμφωνα με τα πραγματικά αποτελέσματα αναφοράς, τα χιλιοστά του δευτερολέπτου στο σχήμα θα πρέπει να είναι σφάλμα γραφείου) και μνήμη 263 KB, ενώ το PooledList <T>χρειάζεται μόνο μνήμη 36us και 0KB.
Συγκεντρωτικό Λεξικό<TKey, TValue>
Προσθέστε 10_0000 στοιχεία στο λεξικό σε έναν βρόχο στο Benchmark, . Το εγγενές λεξικό .NET<TKey, TValue> απαιτεί 11 ms και 13 MB μνήμης, ενώ το PooledDictionary<TKey, TValue> απαιτεί μόνο 7 ms και 0 MB μνήμης.
PooledSet<T>
Κάντε βρόχο στη συλλογή κατακερματισμού στο Benchmark προσθέστε 10_0000 στοιχεία, . Το εγγενές HashSet <T>.NET απαιτεί 5348ms και 2MB, ενώ το PooledSet <T>απαιτεί μόνο 4723ms και 0MB μνήμη.
Συγκεντρωτική στοίβα<T>
Κάντε βρόχο στη στοίβα στο Benchmark για να προσθέσετε 10_0000 στοιχεία, . Το εγγενές .NET PooledStack <T>απαιτεί 1079ms και 2MB, ενώ το PooledStack <T>απαιτεί μόνο 633ms και 0MB μνήμης.
PooledQueue<T>
Κάντε βρόχο στους βρόχους στο Benchmark για να προσθέσετε 10_0000 στοιχεία στην ουρά, . Το εγγενές <T>.NET PooledQueue απαιτεί 681ms και 1MB, ενώ το PooledQueue <T>απαιτεί μόνο 408ms και 0MB μνήμης.
Η σκηνή δεν απελευθερώνεται χειροκίνητα
Επιπλέον, αναφέραμε παραπάνω ότι ο τύπος συγκεντρωτικής συλλογής πρέπει να κυκλοφορήσει, αλλά δεν έχει σημασία αν δεν κυκλοφορήσει, γιατί το GC θα ανακυκλωθεί.
Τα αποτελέσματα του Benchmark έχουν ως εξής:
Το συμπέρασμα μπορεί να εξαχθεί από τα παραπάνω αποτελέσματα του Benchmark.
Η έγκαιρη απελευθέρωση της συλλογής τύπου Pooled μόλις ενεργοποιεί το GC και εκχωρεί μνήμη, από το παραπάνω γράφημα εκχωρεί μόνο 56 Byte μνήμης. Ακόμα και αν η συλλογή συγκεντρωτικών τύπων δεν κυκλοφορήσει, επειδή εκχωρεί μνήμη από το χώρο συγκέντρωσης, θα επαναχρησιμοποιήσει τη μνήμη κατά τη διάρκεια της λειτουργίας επέκτασης αλλαγής μεγέθους και θα παραλείψει το βήμα προετοιμασίας της μνήμης εκχώρησης GC, το οποίο είναι σχετικά γρήγορο. Το πιο αργό είναι να χρησιμοποιήσετε τον κανονικό τύπο συλλογής, κάθε λειτουργία επέκτασης ReSize πρέπει να εφαρμοστεί για νέο χώρο μνήμης και το GC πρέπει επίσης να ανακτήσει τον προηγούμενο χώρο μνήμης.
Ανάλυση αρχών
Εάν έχετε διαβάσει την προηγούμενη ανάρτησή μου στο ιστολόγιο, θα πρέπει να ορίσετε το αρχικό μέγεθος για τους τύπους συλλογής και να αναλύσετε την αρχή υλοποίησης του Λεξικού C#, μπορείτε να γνωρίζετε ότι οι προγραμματιστές .NET BCL χρησιμοποιούν τις υποκείμενες δομές δεδομένων αυτών των βασικών τύπων συλλογής ως πίνακες για τυχαία πρόσβαση υψηλής απόδοσης, ας πάρουμε <T>τη Λίστα ως παράδειγμα.
Δημιουργήστε έναν νέο πίνακα για να αποθηκεύσετε τα προστιθέμενα στοιχεία. Εάν δεν υπάρχει αρκετός χώρος στη συστοιχία, ενεργοποιείται η λειτουργία επέκτασης για να ζητηθεί διπλάσιο μέγεθος χώρου. Ο κώδικας κατασκευής έχει ως εξής και μπορείτε να δείτε ότι είναι ένας γενικός πίνακας που δημιουργήθηκε απευθείας:
Επομένως, εάν θέλετε να συγκεντρώσετε μνήμη, χρειάζεται μόνο να αλλάξετε τη θέση όπου χρησιμοποιείται η νέα εφαρμογή λέξης-κλειδιού στη βιβλιοθήκη κλάσεων για να χρησιμοποιήσετε ομαδοποιημένη εφαρμογή. Εδώ το μοιράζομαι μαζί σας. Το NET BCL είναι ένας τύπος που ονομάζεται ArrayPool, ο οποίος παρέχει μια δεξαμενή πόρων συστοιχίας επαναχρησιμοποιήσιμων γενικών περιπτώσεων, οι οποίες μπορούν να χρησιμοποιηθούν για τη μείωση της πίεσης στο GC και τη βελτίωση της απόδοσης σε περίπτωση συχνής δημιουργίας και καταστροφής συστοιχιών.
Το υποκείμενο επίπεδο του τύπου Pooled είναι η χρήση του ArrayPool για κοινή χρήση ομάδων πόρων και από τον κατασκευαστή του, μπορούμε να δούμε ότι χρησιμοποιεί το ArrayPool από προεπιλογή<T>. Κοινόχρηστο για την εκχώρηση αντικειμένων πίνακα και φυσικά μπορείτε επίσης να δημιουργήσετε το δικό σας ArrayPool για να το χρησιμοποιήσετε.
Επιπλέον, κατά την εκτέλεση μιας λειτουργίας προσαρμογής χωρητικότητας (επέκταση), ο παλιός πίνακας επιστρέφει στο χώρο συγκέντρωσης νημάτων και ο νέος πίνακας αποκτάται επίσης από τον χώρο συγκέντρωσης.
Επιπλέον, ο συγγραφέας χρησιμοποιεί το Span για να βελτιστοποιήσει τα API όπως το Add και το Insert για να τους προσφέρει καλύτερη απόδοση τυχαίας πρόσβασης. Επιπλέον, προστέθηκε το API της σειράς TryXXX, ώστε να μπορείτε να το χρησιμοποιήσετε με πιο βολικό τρόπο. Για παράδειγμα, η <T>κλάση List <T>έχει έως και 170 τροποποιήσεις σε σύγκριση με την PooledList.
περίληψη
Στην πραγματική μας διαδικτυακή χρήση, μπορούμε να αντικαταστήσουμε τον εγγενή τύπο συλλογής με τον τύπο συλλογής που παρέχεται από το Pooled, κάτι που είναι πολύ χρήσιμο για τη μείωση της χρήσης μνήμης και του λανθάνοντος χρόνου P95. Επίσης, ακόμα κι αν ξεχάσετε να το κυκλοφορήσετε, η απόδοση δεν θα είναι πολύ χειρότερη από τη χρήση του εγγενούς τύπου συλλογής. Φυσικά, η καλύτερη συνήθεια είναι να το απελευθερώσετε εγκαίρως.
Αρχικός:Η σύνδεση με υπερσύνδεσμο είναι ορατή.
|