Μέτρηση#

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

Το εργαλείο που δεν λειτουργεί: Devel::NYTProf#

Το Devel::NYTProf είναι ο τυπικός προφίλερ γραμμής και υπορουτίνας στην upstream Perl. Είναι άρθρωμα XS - αγκιστρώνεται στον διερμηνευτή μέσω μιας επέκτασης C. Το pperl δεν διαθέτει στρώμα XS, οπότε το Devel::NYTProf δεν φορτώνεται. Ούτε φορτώνεται οποιοσδήποτε άλλος προφίλερ βασισμένος σε XS (Devel::DProf, τα μονοπάτια XS του Devel::Profiler).

Αυτό δεν είναι ένα κενό που πρέπει να παρακαμφθεί με μια υλοποίηση του μορφότυπου εξόδου του NYTProf αμιγώς σε Perl. Το pperl έχει τον δικό του προφίλερ, ενσωματωμένο στο runtime, που βλέπει τι πραγματικά εκτελεί ο διερμηνευτής - πλήθη ops και τον χρόνο που δαπανάται στο καθένα. Χρησιμοποιήστε τον.

Where the time goes: the JIT log#

pperl does not currently ship a line-or-sub profiler of its own (the one it once had belonged to a retired execution engine and never saw the current runtime). What it does ship is precise visibility into the optimization layer, which for pperl-specific tuning is usually the question you are actually asking: is my hot loop compiled, and if not, why not?

  • --jit-stats prints counters at exit: candidates found, loops compiled, entries run compiled, guard failures, deopts.

  • PPERL_JIT_LOG=/path/file appends one line per compiler event. The compile skip reason=... lines name the exact operation that kept a loop interpreted.

For symbol-level CPU attribution, use the platform profiler on a release build - perf record ./target/release/pperl script.pl then perf report. pperl is a native binary; perf sees straight through it, including JIT-compiled frames.

Χρονομέτρηση ολόκληρου του προγράμματος: ο εκτελεστής benchmark#

Όταν το ερώτημα είναι «πόσο γρήγορο είναι το pperl σε αυτό, έναντι της συστημικής perl, με και χωρίς JIT και παραλληλισμό», ο εκτελεστής benchmark του έργου το απαντά απευθείας. Το bench/run-perlbench τρέχει πάντα το /usr/bin/perl ως γραμμή αναφοράς (κανονικοποιημένη στο 100) και συγκρίνει το pperl σε έως τέσσερις διαμορφώσεις.

./bench/run-perlbench                # no JIT, no parallel
./bench/run-perlbench +J             # add the JIT-enabled run
./bench/run-perlbench +P             # add the parallel run
./bench/run-perlbench +J+P           # all four combinations
./bench/run-perlbench -t arith       # only benchmarks matching "arith"

Κάθε εκτελεστής μοιράζεται έναν κοινό αριθμό επαναλήψεων, που προκύπτει από τον ρυθμό κενού βρόχου του πιο αργού εκτελεστή, ώστε οι αναφερόμενοι λόγοι να συγκρίνουν όμοια με όμοια. Οι στήλες +J / +P είναι ακριβώς η σύγκριση που θέλετε όταν αποφασίζετε αν ένα κομμάτι κώδικα ωφελείται από το JIT ή τον παραλληλοποιητή: αν το +J δεν είναι γρηγορότερο από την απλή εκτέλεση, ο βρόχος δεν μεταγλωττίζεται, και το Γράφοντας γρήγορο pperl εξηγεί γιατί και τι να αλλάξετε.

Εργαλεία αμιγούς Perl που λειτουργούν: Benchmark και Time::HiRes#

Τα κλασικά εργαλεία μέτρησης εντός προγράμματος είναι αμιγής Perl. Τρέχουν στο pperl μέσω του κανονικού μονοπατιού αρθρωμάτων - το Benchmark είναι προσβάσιμο στο τυπικό μονοπάτι ενσωμάτωσης και εκτελείται ως Perl στον διερμηνευτή του pperl· το Time::HiRes παρέχεται natively. Και τα δύο συμπεριφέρονται ακριβώς όπως περιμένει ένας προγραμματιστής Perl.

Το Benchmark για A/B σύγκριση δύο τρόπων γραφής της ίδιας ρουτίνας:

use Benchmark qw(cmpthese);

my @data = (1 .. 100_000);

cmpthese(-3, {            # run each for ~3 CPU-seconds
    grep_count => sub { my $n = grep { $_ % 2 == 0 } @data },
    loop_count => sub { my $n = 0; $_ % 2 or $n++ for @data },
});

Το Time::HiRes για περιστασιακή χρονομέτρηση πραγματικού χρόνου ενός μεμονωμένου μπλοκ:

use Time::HiRes qw(time);

my $t0 = time;
do_the_work();
printf "took %.3f s\n", time - $t0;

Μια προειδοποίηση που μετράει περισσότερο στο pperl απ” ό,τι στο upstream: το Benchmark οδηγεί τον κώδικά σας καλώντας μια αναφορά κώδικα ξανά και ξανά, και αυτή η αποστολή ανά κλήση είναι το κόστος του ίδιου του Benchmark, όχι του κώδικά σας. Ένας βρόχος μέσα στο callback εξακολουθεί να μεταγλωττίζεται με JIT (οι βρόχοι μεταγλωττίζονται όπου κι αν ζουν, σε εμβέλεια αρχείου ή σε σώμα υπορουτίνας), αλλά για μικρά σώματα το κόστος κλήσης του πλαισίου κυριαρχεί και η σύγκριση υποτιμά την πραγματική ταχύτητα. Όταν αυτό που χρονομετράτε είναι ένας βρόχος που θα έπρεπε να περάσει από JIT, προτιμήστε το bench/run-perlbench ή ένα διάστημα Time::HiRes γύρω από τον βρόχο επιτόπου (δείτε Μεταγλώττιση JIT).

Απενεργοποίηση των βελτιστοποιητών για την απομόνωση μιας αιτίας#

Δύο σημαίες σάς επιτρέπουν να αποδώσετε μια επιτάχυνση - ή μια επιβράδυνση - στο σωστό στρώμα:

pperl --no-jit script.pl          # interpreter only, no native compilation
pperl --no-parallel script.pl     # single-threaded, no Rayon dispatch

Τρέξτε ένα πρόγραμμα με τρεις τρόπους - προεπιλογή, --no-jit, --no-parallel - και οι διαφορές σάς λένε ποιος βελτιστοποιητής κουβαλά την εργασία. Αν το --no-jit μετά βίας αλλάζει τον χρόνο, το JIT δεν εμπλεκόταν σε αυτόν τον κώδικα, και η μορφή του βρόχου είναι αυτό που πρέπει να εξετάσετε. Αν το --no-parallel μετά βίας τον αλλάζει, ο παραλληλοποιητής απέρριψε τον βρόχο - συνήθως λόγω μιας παρενέργειας που δεν μπόρεσε να αποκλείσει (δείτε Παράλληλη εκτέλεση).

Μια πειθαρχία μέτρησης#

  1. Μετρήστε τον χρόνο του προγράμματος στην προεπιλογή έναντι --no-jit έναντι --no-parallel. Οι διαφορές σάς λένε ποιο στρώμα κουβαλά τη δουλειά.

  2. Αν μια θερμή ρουτίνα είναι ένας βρόχος που περιμένατε να μεταγλωττιστεί, επιβεβαιώστε με run-perlbench +J ή χρονομετρώντας τον με --no-jit έναντι της προεπιλογής. Ένας βρόχος που δεν επιταχύνεται υπό το JIT είναι κακοσχηματισμένος, όχι αργός.

  3. Μόνο τώρα αλλάξτε κώδικα. Ξαναμετρήστε με τον ίδιο τρόπο. Κρατήστε την αλλαγή αν ο αριθμός κινήθηκε και το πρόγραμμα εξακολουθεί να παράγει την ίδια έξοδο.

Οι ισχυρισμοί περί απόδοσης είναι φθηνοί· η αναφορά του προφίλερ και ο λόγος του benchmark δεν είναι. Εμπιστευτείτε τη μέτρηση.

Δείτε επίσης#

  • Γράφοντας γρήγορο pperl - αφού βρείτε τον θερμό βρόχο, πώς να τον διαμορφώσετε ώστε να τον αναλάβουν το JIT και ο παραλληλοποιητής.

  • Ιδιωματισμοί - ποιες κλασικές βελτιστοποιήσεις αξίζει ακόμη να εφαρμόσετε προτού καταφύγετε στον προφίλερ.

  • Μεταγλώττιση JIT - τι μεταγλωττίζει το JIT και γιατί οι χρονομετρήσεις ερμηνευμένων ops εξαφανίζονται για τους μεταγλωττισμένους βρόχους.

  • Παράλληλη εκτέλεση - τι ασκούν η στήλη +P και η σημαία --no-parallel.

  • Απόδοση regex - αν ο προφίλερ δείχνει σε ένα regexp, η αιτία και η θεραπεία βρίσκονται εκεί, όχι εδώ.