Benchee

Αυτή η μετάφραση είναι πλήρως ενημερωμένη.

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

Πίνακας περιεχομένων

Σχετικά με το Benchee

Παρόλο που υπάρχει μια συνάρτηση στην Erlang που μπορεί να χρησιμοποιηθεί για πολύ βασικές μετρήσεις του χρόνου εκτέλεσης μιας συνάρτησης, δεν είναι το ίδιο καλή στη χρήση σε σχέση με μερικά από τα διαθέσιμα εργαλεία και δεν σας δίνει πολλαπλές μετρήσεις για να πάρετε καλά στατιστικά. Για αυτό το λόγο θα χρησιμοποιήσουμε το Benchee. To Benchee μας παρέχει ένα εύρος στατιστικών με εύκολες συγκρίσεις μεταξύ σεναρίων, ένα τρομερό χαρακτηριστικό που μας επιτρέπει να ελέγξουμε διαφορετικές εισόδους στις συναρτήσεις που μετράμε και διάφορους μορφοποιητές για να εμφανίσουμε τα αποτελέσματά μας, καθώς και την ικανότητα να γράψουμε τον δικό μας μορφοποιητή αν το επιθυμούμε.

Χρήση

Για να προσθέσουμε το Benchee στο project μας, προσθέστε το σαν εξάρτηση στο mix.exs αρχείο σας:

defp deps do
  [{:benchee, "~> 1.0", only: :dev}]
end

Then we call:

$ mix deps.get
...
$ mix compile

Η πρώτη εντολή θα κατεβάσει και εγκαταστήσει το Benchee. Μπορεί να σας ζητηθεί να εγκαταστήσετε το Hex μαζί με αυτό. Η δεύτερη συντάσσει την εφαρμογή Benchee. Τώρα είμαστε έτοιμοι να γράψουμε την πρώτη μας συγκριτική αξιολόγηση!

Μια σημαντική σημείωση πριν ξεκινήσουμε: Όταν αξιολογούμε, είναι πολύ σημαντικό να μην χρησιμοποιούμε το iex από τη στιγμή που συμπεριφέρεται διαφορετικά και είναι συχνά πιο αργό από τη συμπεριφορά του κώδικά σας στην παραγωγή. Έτσι, ας δημιουργήσουμε ένα αρχείο που θα ονομάσουμε benchmark.exs και σε αυτό το αρχείο ας βάλουμε τον παρακάτω κώδικα:

list = Enum.to_list(1..10_000)
map_fun = fn i -> [i, i * i] end

Benchee.run(%{
  "flat_map"    => fn -> Enum.flat_map(list, map_fun) end,
  "map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end
})

Τώρα για να τρέξουμε την συγκριτική αξιολόγηση, καλούμε:

$ mix run benchmark.exs

Και θα πρέπει να δούμε κάτι σαν την παρακάτω έξοδο στην κονσόλα σας:

Operating System: Linux
CPU Information: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
Number of Available Cores: 8
Available memory: 15.61 GB
Elixir 1.8.1
Erlang 21.3.2

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 14 s

Benchmarking flat_map...
Benchmarking map.flatten...

Name                  ips        average  deviation         median         99th %
flat_map           2.40 K      416.00 μs    ±12.88%      405.67 μs      718.61 μs
map.flatten        1.24 K      806.20 μs    ±20.65%      752.52 μs     1186.28 μs

Comparison:
flat_map           2.40 K
map.flatten        1.24 K - 1.94x slower +390.20 μs

Φυσικά οι πληροφορίες του συστήματός σας και τα αποτελέσματα μπορεί να διαφέρουν ανάλογα με τα χαρακτηριστικά του μηχανήματος που τρέχετε την συγκριτική αξιολόγηση, αλλά αυτές οι γενικές πληροφορίες θα πρέπει να είναι εκεί.

Με μια πρώτη ματιά, ο τομέας Comparison μας δείχνει ότι η εκδοχή μας της map.flatten είναι 1.94x φορές πιο αργή από την flat_map. Επίσης δείχνει ότι κατά μέσο όρο είναι περίπου 390 μικροδευτερόλεπτα πιο αργή, το οποίο αλλάζει τις προοπτικές μας. Πολύ χρήσιμο να το γνωρίζουμε! Αλλά, ας ρίξουμε μια ματιά και στα άλλα στατιστικά που παίρνουμε:

  • ips - σημαίνει “iterations per second - επαναλήψεις το δευτερόλεπτο”, το οποίο μας λέει πόσο συχνά η δοθείσα συνάρτηση μπορεί να εκτελεστεί σε ένα δευτερόλεπτο. Για αυτή τη μέτρηση, ένας μεγαλύτερος αριθμός είναι καλύτερος.
  • average - είναι ο μέσος χρόνος εκτέλεσης της δοθείσας συνάρτησης. Για αυτή τη μέτρηση, ένας χαμηλότερος αριθμός είναι καλύτερος.
  • deviation - είναι η τυπική απόκλιση, η οποία μας λέει πόσο πολύ αποκλείνουν τα αποτελέσματα κάθε επανάληψης. Εδώ δίνεται σαν ποσοστό επί του μέσου χρόνου εκτέλεσης.
  • median - όταν όλες οι μετρημένες τιμές ταξινομηθούν, αυτή είναι η μεσαία τιμή (ή η μέση τιμή των δύο μεσέων τιμών όταν ο αριθμός δειγμάτων είναι ζυγός). Εξ’ αιτίας περιβαλλοντικών ανακολουθιών αυτή η τιμή θα είναι πιο σταθερή από την average, και πιο ικανή να αντικατοπτρίσει την κανονική απόδοση του κώδικά σας στην παραγωγή. Για αυτή τη μέτρηση, ένας χαμηλότερος αριθμός είναι καλύτερος.
  • 99th % - Το 99% όλων των μετρήσεων είναι πιό γρήγορες απο αυτήν, κάνοντάς την απόδοση χειρ΄ότερης περίπτωσης. Όσο χαμηλότερος ο αριθμός, τόσο καλύτερα.

Υπάρχουν επίσης άλλα διαθέσιμα στατιστικά, αλλά αυτές οι τέσσερις είναι συχνά οι πιό χρήσιμες και συχνά χρησιμοποιούμενες για συγκριτικές αξιολογήσεις, γι’ αυτό και εμφανίζονται στον προκαθορισμένο μορφοποιητή. Για να μάθετε περισσότερα για τις άλλες διαθέσιμες μετρήσεις, ελέγξτε την τεκμηρίωση στα hexdocs.

Ρυθμίσεις

Ένα από καλύτερα μέρη του Benchee είναι οι διαθέσιμες επιλογές ρυθμίσεων. Θα δούμε πρώτα τα βασικά από τη στιγμή που δεν απαιτούν παραδείγματα κώδικα, και τότε θα δείξουμε πως να χρησιμοποιούμε ένα από τα καλύτερα χαρακτηριστικά του Benchee - τις εισαγωγές.

Βασικές

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

Benchee.run(%{"example function" => fn -> "hi!" end},
  warmup: 4,
  time: 10,
  inputs: nil,
  parallel: 1,
  formatters: [Benchee.Formatters.Console],
  print: [
    benchmarking: true,
    configuration: true,
    fast_warning: true
  ],
  console: [
    comparison: true,
    unit_scaling: :best
  ]
)

Οι διαθέσιμες επιλογές είναι οι ακόλουθες hexdocs).

  • warmup - ο χρόνος σε δευτερόλεπτα για τον οποίο ένα σενάριο συγκριτικής αξιολόγησης θα πρέπει να τρέξει χωρίς να υπολογίζεται ο χρόνος πριν ξεκινήσουν οι πραγματικές μετρήσεις. Αυτό εξομοιώνει ένα “ζεστό” σύστημα που τρέχει. Προκαθορισμένο είναι το 2.
  • time - ο χρόνος σε δευτερόλεπτα για το πόσο κάθε ξεχωριστό σενάριο συγκριτικής αξιολόγησης θα πρέπει να τρέξει και να μετρηθεί. Προκαθορισμένο είναι το 5.
  • memory_time - ο χρόνος σε δευτερόλεπτα για το πόσο η κατανάλωση μνήμης θα πρέπει να μετρηθεί για κάθε σενάριο συγκριτικής αξιολόγησης. Αυτό είναι κάτι που θα κοιτάξουμε ξανά αργότερα. Προκαθορισμένο είναι το 0.
  • inputs - ο χάρτης αλφαριθμητικών που αντιπροσωπεύει το όνομα εισαγωγής σαν το κλειδί και την πραγματική είσοδο σαν τιμή. Μπορεί επίσης να είναι μια λίστα από τούπλες με την μορφή {input_name, actual_value}. Προκαθορισμένο το nil (χωρίς εισαγωγές). Θα καλύψουμε αυτή την επιλογή σε βάθος στον επόμενο τομέα.
  • parallel - ο αριθμός διεργασιών προς χρήση για την συγκριτική αξιολόγηση των συναρτήσεων σας. Έτσι, αν ορίσετε parallel: 4, τότε 4 διεργασίες θα ξεκινήσουν οι οποίες θα εκτελούν όλες την ίδια συνάρτηση για τον δοθέντα χρόνο. Όταν αυτές τελειώσουν, τότε 4 νέες διεργασίες θα ξεκινήσουν για την επόμενη συνάρτηση. Αυτό σας δίνει περισσότερα δεδομένα στον ίδιο χρόνο, αλλά επίσης βάζει ένα φόρτο στο σύστημά σας ο οποίος παρεμβάλλεται στα αποτελέσματα των συγκριτικών αξιολογήσεων. Αυτό μπορεί να είναι χρήσιμο στην εξομοίωση ενός συστήματος σε φόρτο το οποίο μερικές φορές είναι χρήσιμο, αλλά πρέπει να χρησιμοποιείται με προσοχή καθώς μπορεί να επηρεάσει τα αποτελέσματα με απρόβλεπτο τρόπο. Προκαθορισμένο το 1 (το οποίο σημαίνει όχι παράλληλη εκτέλεση).
  • formatters - μια λίστα μορφοποιητών είτε σαν μια ενότητα που εφαρμόζει την συμπεριφορά του μορφοποιητή, μια τούπλα της συγκεκριμένης ενότητας και επιλογών που θα έπρεπε να δέχεται ή συναρτήσεις μορφοποιητών. Τρέχουν όταν χρησιμοποιείτε την Benchee.run/2. Οι συναρτήσεις πρέπει να αποδέχονται ένα όρισμα (το οποίο είναι η σουίτα συγκριτικών αξιολογήσεων με όλα τα δεδομένα) και τότε να τα χρησιμοποιούν για να παράγουν έξοδο. Προκαθορισμένο είναι ο ενσωματωμένος μορφοποιητής κονσόλας που καλεί την Benchee.Formatters.Console. Θα το καλύψουμε αυτό σε επόμενο τομέα.
  • measure_function_call_overhead - μετράει πόσο χρόνο χρειάζεται μια κλήση κενής συνάρτησης και το αφαιρεί κάθε φορά από τον χρόνο εκτέλεσης. Βοηθά με την ακρίβεια πολύ γρήγορων σεναρίων συγκριτικής αξιολόγησης. Προκαθορισμένη τιμή είναι το true.
  • pre_check - το να τρέξει ή όχι κάθε εργασία με κάθε εισαγωγή - συμπεριλαμβάνοντας όλα τα δοθέντα σενάρια - πριν μετρηθούν τα σενάρια συγκριτικής αξιολόγησης ώστε να εξασφαλίσει οτι ο κώδικας εκτελείται χωρίς σφάλματα. Αυτό μπορεί να μας κερδίσει χρόνο κατά την δημιουργία των suits σας. Προκαθορισμένη τιμή είναι το false.
  • save - προσδιορίζει μια διαδρομή όπου θα αποθηκευτούν τα αποτελέσματα της τρέχουσας σουίτας συγκριτικής αξιολόγησης, έχοντας ως ετικέτα την προκαθορισμένη ετικέτα. Δείτε το Saving & Loading στα Benchee docs.
  • load - φορτώστε μια αποθηκευμένη σουίτα ή σουίτες ώστε να συγκρίνετε τις τρέχουσες συγκριτικές σας αξιολογήσεις. Μπορεί να είναι μια συμβολοσειρά ή λίστα από συμβολοσειρές ή μοτίβα. Δείτε το Saving & Loading στα Benchee docs.
  • print - ένας χάρτης ή μια λίστα λέξεων κλειδιά με τις ακόλουθες επιλογές σαν άτομα για τα κλειδιά και τιμές είτε true ή false. Αυτό μας επιτρέπει να ελέγχουμε αν η έξοδος ορισμένη από το άτομο θα εκτυπωθεί κατά τη διάρκεια της κανονικής διεργασίας συγκριτικής αξιολόγησης. Όλες οι επιλογές είναι ενεργοποιημένες εξ’ ορισμού (true). Οι επιλογές είναι:
    • benchmarking - εκτυπώση όταν το Benchee ξεκινάει την αξιολόγηση μιας νέας εργασίας.
    • configuration - μια συλλογή ρυθμισμένων επιλογών αξιολόγησης συμπεριλαμβανομένου του εκτιμώμενου χρόνου εκτέλεσης εκτυπώνεται πριν ξεκινήσει η αξιολόγηση.
    • fast_warning - εμφανίζονται προειδοποιήσεις αν οι συναρτήσεις εκτελούνται πολύ γρήγορα, το οποίο ενδεχομένως να οδηγεί σε ανακριβείς μετρήσεις.
  • unit_scaling - η στρατηγική κλιμάκωσης μονάδας για τις διάρκειες και τις μετρήσεις. Όταν αυξομειώνεται μια τιμή, το Benchee βρίσκει την “καλύτερη” μονάδα μέτρησης (η μεγαλύτερη μονάδα για την οποία το αποτέλεσμα είναι τουλάχιστον 1). Για παράδειγμα, το 1_200_000 αλλάζει σε 1.2 M, ενώ το 800_000 σε 800 K. Η στρατηγική κλιμάκωσης μονάδας καθορίζει πως το Benchee επιλέγει την καλύτερη μονάδα για μια μεγάλη λίστα τιμών, όταν οι επι μέρους τιμές στη λίστα έχουν πολλές διαφορετικές ιδανικές μονάδες μέτρησης. Υπάρχουν τέσσερις στρατηγικές, όλες δοθείσες σαν άτομα, προκαθορισμένο το :best:
    • best - η πιο συχνή ιδανική μονάδα θα χρησιμοποιηθεί. Μια ισοπαλία θα οδηγήσει στην επιλογή της μεγαλύτερης μονάδας.
    • largest - η μεγαλύτερη ιδανική μονάδα θα χρησιμοποιηθεί.
    • smallest - η μικρότερη ιδανική μονάδα θα χρησιμοποιηθεί.
    • none - δεν θα γίνει καμία κλιμάκωση. Οι διάρκειες θα εμφανιστούν σε μικροδευτερόλεπτα, οι ips μετρήσεις θα εμφανιστούν χωρίς μονάδες.
  • :before_scenario/after_scenario/before_each/after_each - δεν θα εμβαθύνουμε σε αυτές εδώ αλλά αν θέλετε να κάνετε κάτι πριν/μετά την συνάρτηση συγκριτικής αξιολόγησής σας χωρίς αυτό να μετρηθεί δείτε το Benchee’s hooks section

Είσοδοι

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

Έτσι, ας δούμε το αρχικό μας παράδειγμα ξανά:

list = Enum.to_list(1..10_000)
map_fun = fn i -> [i, i * i] end

Benchee.run(%{
  "flat_map"    => fn -> Enum.flat_map(list, map_fun) end,
  "map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end
})

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

map_fun = fn i -> [i, i * i] end

inputs = %{
  "small list" => Enum.to_list(1..100),
  "medium list" => Enum.to_list(1..10_000),
  "large list" => Enum.to_list(1..1_000_000)
}

Benchee.run(
  %{
    "flat_map" => fn list -> Enum.flat_map(list, map_fun) end,
    "map.flatten" => fn list -> list |> Enum.map(map_fun) |> List.flatten() end
  },
  inputs: inputs
)

Θα παρατηρήσετε δύο διαφορές. Πρώτον, τώρα έχουμε ένα χάρτη inputs που περιέχει την πληροφορία για τις εισαγωγές των συναρτήσεών μας. Αυτό τον χάρτη εισόδου τον περνάμε σαν επιλογή ρυθμίσεων στην Benchee.run/2.

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

fn -> Enum.flat_map(list, map_fun) end

τώρα έχουμε:

fn list -> Enum.flat_map(list, map_fun) end

Ας το ξανατρέξουμε χρησιμοποιώντας:

$ mix run benchmark.exs

Τώρα θα πρέπει να δείτε έξοδο στην κονσόλα σας ως εξής:

Operating System: Linux
CPU Information: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
Number of Available Cores: 8
Available memory: 15.61 GB
Elixir 1.8.1
Erlang 21.3.2

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
parallel: 1
inputs: large list, medium list, small list
Estimated total run time: 42 s

Benchmarking flat_map with input large list...
Benchmarking flat_map with input medium list...
Benchmarking flat_map with input small list...
Benchmarking map.flatten with input large list...
Benchmarking map.flatten with input medium list...
Benchmarking map.flatten with input small list...

##### With input large list #####
Name                  ips        average  deviation         median         99th %
flat_map            13.20       75.78 ms    ±25.15%       71.89 ms      113.61 ms
map.flatten         10.48       95.44 ms    ±19.26%       96.79 ms      134.43 ms

Comparison:
flat_map            13.20
map.flatten         10.48 - 1.26x slower +19.67 ms

##### With input medium list #####
Name                  ips        average  deviation         median         99th %
flat_map           2.66 K      376.04 μs    ±23.72%      347.29 μs      678.17 μs
map.flatten        1.75 K      573.01 μs    ±27.12%      512.48 μs     1076.27 μs

Comparison:
flat_map           2.66 K
map.flatten        1.75 K - 1.52x slower +196.98 μs

##### With input small list #####
Name                  ips        average  deviation         median         99th %
flat_map         266.52 K        3.75 μs   ±254.26%        3.47 μs        7.29 μs
map.flatten      178.18 K        5.61 μs   ±196.80%        5.00 μs       10.87 μs

Comparison:
flat_map         266.52 K
map.flatten      178.18 K - 1.50x slower +1.86 μs

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

Μορφοποιητές

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

Άλλοι μορφοποιητές

Το Benchee έχει ένα μορφοποιητή κονσόλας προκαθορισμένο, ο οποίος είναι αυτός που έχουμε δει ήδη, αλλά υπάρχουν άλλοι τρεις επίσημα υποστηριζόμενοι - benchee_csv, benchee_json και benchee_html. Κάθε ένας από αυτούς κάνει ακριβώς αυτό που θα περιμένατε, το οποίο είναι να γράφει τα αποτελέσματα στο αντίστοιχο τύπο αρχείου ώστε να μπορείτε να δουλέψετε με τα αποτελέσματα περαιτέρω σε οποιοδήποτε φορμάτ θέλετε.

Κάθε ένας από αυτούς τους μορφοποιητές είναι ένα ξεχωριστό πακέτο, έτσι για να τους χρησιμοποιήσετε πρέπει να τους προσθέσετε σαν εξαρτήσεις στο αρχείο mix.exs σας ως εξής:

defp deps do
  [
    {:benchee_csv, "~> 1.0", only: :dev},
    {:benchee_json, "~> 1.0", only: :dev},
    {:benchee_html, "~> 1.0", only: :dev}
  ]
end

Παρόλο που οι benchee_json και benchee_csv είναι πολύ απλές, η benchee_html είναι στην πραγματικότητα πολύ πλήρης σε χαρακτηριστικά! Μπορεί να σας βοηθήσει να δημιουργήσετε όμορφα γραφήματα και διαγράμματα από τα αποτελέματά σας πολύ εύκολα και ακόμα και να τα εξάγετε σαν εικόνες PNG. Μπορείτε να δείτε το ένα παράδειγμα html αναφοράς αν σας ενδιαφέρει, περιέχει γραφήματα σαν αυτό:

benchee_html graph export sample

Όλοι οι τρεις μορφοποιητές είναι πολύ καλά τεκμηριωμένοι στις αντίστοιχες σελίδες τους στο GitHub, έτσι δεν θα καλύψουμε τις λεπτομέρειες τους εδώ.

Προσαρμοσμένοι Μορφοποιητές

Αν οι τέσσερις προσφερόμενοι μορφοποιητές δεν είναι αρκετοί για εσάς, μπορείτε επίσης να γράψετε το δικό σας μορφοποιητή. Το να γράψετε έναν μορφοποιητή είναι σχετικά εύκολο. Πρέπει να γράψετε μια συνάρτηση που δέχεται μια δομή %Benchee.Suite{}, και από αυτή μπορείτε να τραβήξετε ότι πληροφορία θέλετε. Πληροφορίες για το τι περιλαμβάνεται σε αυτή τη δομή μπορούν να βρεθούν στο GitHub ή στα HexDocs. Ο κώδικας είναι πολύ καλά τεκμηριωμένος και εύκολα αναγνώσιμος αν θα θέλατε να δείτε του τι είδους πληροφορίες μπορούν να είναι διαθέσιμες για την εγγραφή των δικών σας μορφοποιητών.

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

defmodule Custom.Formatter do
  def output(suite) do
    suite
    |> format
    |> IO.write()

    suite
  end

  defp format(suite) do
    Enum.map_join(suite.scenarios, "\n", fn scenario ->
      "Average for #{scenario.job_name}: #{scenario.run_time_data.statistics.average}"
    end)
  end
end

Και τότε θα μπορούμενα τρέξουμε τη συγκριτική αξιολόγησή μας ως εξής:

list = Enum.to_list(1..10_000)
map_fun = fn i -> [i, i * i] end

Benchee.run(
  %{
    "flat_map" => fn -> Enum.flat_map(list, map_fun) end,
    "map.flatten" => fn -> list |> Enum.map(map_fun) |> List.flatten() end
  },
  formatters: [&Custom.Formatter.output/1]
)

Και όταν τρέξουμε τώρα με τον τρέχων μορφοποιητή, θα πρέπει να δούμε:

Operating System: Linux
CPU Information: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
Number of Available Cores: 8
Available memory: 15.61 GB
Elixir 1.8.1
Erlang 21.3.2

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 14 s

Benchmarking flat_map...
Benchmarking map.flatten...
Average for flat_map: 419433.3593474056
Average for map.flatten: 788524.9366408596

Μνήμη

Σχεδόν φτάσαμε στο τέλος, αλλά καθ’ όλη αυτή τη διάρκεια, δεν σας δείξαμε μια από τις πιο αξιόλογες ιδιότητες του Benchee: τις μετρήσεις μνήμης!

Το Benchee είναι σε θέση να μετρήσει την κατανάλωση μνήμης, αλλά περιορίζεται στην διεργασία για την οποία εκτελείται η συγκριτική σας αξιολόγηση. Προς το παρόν δεν μπορεί να ιχνηλατήσει την κατανάλωση μνήμης σε άλλες διεργασίες (όπως οι λίστες εργατών).

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

Πως το χρησιμοποιείτε; Απλά χρησιμοποιείτε την επιλογή :memory_time!

Operating System: Linux
CPU Information: Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz
Number of Available Cores: 8
Available memory: 15.61 GB
Elixir 1.8.1
Erlang 21.3.2

Benchmark suite executing with the following configuration:
warmup: 0 ns
time: 0 ns
memory time: 1 s
parallel: 1
inputs: none specified
Estimated total run time: 2 s

Benchmarking flat_map...
Benchmarking map.flatten...

Memory usage statistics:

Name           Memory usage
flat_map          624.97 KB
map.flatten       781.25 KB - 1.25x memory usage +156.28 KB

**All measurements for memory usage were the same**

Όπως μπορείτε να δείτε, το Benchee δεν θα εμφανίσει όλα τα στατιστικά αφού όλα τα δείγματα που πάρθηκαν ήταν ίδια. Αυτό συναντάται συχνά αν οι συναρτήσεις σας δεν περιέχουν κάποιο μέγεθος τυχαιότητας. Και ποιό άλλωστε θα ήταν το όφελος των στατιστικών αν μας δίναν το ίδιο αποτέλεσμα συνεχώς;

Caught a mistake or want to contribute to the lesson? Edit this page on GitHub!