Moduł Benchmark

Uniwersytet Gdański - Instytut Matematyki - Zakład Informatyki - Strona domowa

Obsługa modułu Benchmark

Co to jest

Moduł Benchmark to narzędziowy moduł obiektowo-strukturalny, przydatny do pomiarów czasu wykonania kodu. Można korzystać z niego na dwa sposoby: wywołując udostępnione procedury i funkcje, lub tworząc obiekty klasy Benchmark. Poniżej opisałem krótko oba sposoby i podałem kilka przykładów zastosowania.

use Benchmark qw(:all) ; # sprawdź, jak długo wykonuje się kod "code" timethis ($count, "code"); # użyj kodu perla w napisach zamiast code1, code2 itp. timethese($count, { 'Name1' => '...code1...', 'Name2' => '...code2...', }); # ... lub użyj referencji do anonimowych procedur... timethese($count, { 'Name1' => sub { ...code1... }, 'Name2' => sub { ...code2... }, }); # cmpthese także może użyć obu metod cmpthese($count, { 'Name1' => '...code1...', 'Name2' => '...code2...', }); cmpthese($count, { 'Name1' => sub { ...code1... }, 'Name2' => sub { ...code2... }, }); # ...lub może być użyte w dwóch etapach... $results = timethese($count, { 'Name1' => sub { ...code1... }, 'Name2' => sub { ...code2... }, }, 'none' ); cmpthese( $results ) ; $t = timeit($count, '...inny kod...') print "$count wykonań innego kodu zajęło: ",timestr($t),"\n"; $t = countit($time, '...inny kod...') $count = $t->iters ; print "$count wykonań innego kodu trwało: ",timestr($t),"\n"; # włączenie wysokorozdzielczego zegara, jeżeli możliwe use Benchmark ':hireswallclock';

Interfejs strukturalny: import domyślny

Interfejs strukturalny stanowi w tym module kilka funkcji, które mogą być używane bez korzystania z obiektowości. Niektóre z nich, opisane poniżej, są przez moduł domyślnie importowane do przestrzeni nazw bieżącego pakietu.

timethis
Funkcja uruchamia określoną ilość razy jakąś porcję kodu. Prawidłowe wywołanie musi odbywać się zgodnie z kodem: timethis( LICZNIK, KOD, [ TYTUŁ, [ STYL ]] ); Pomiar polega na tym, że funkcja wykonuje podany KOD tyle razy, ile wynosi wartość argumentu LICZNIK (liczba wykonań kodu), a następnie wypisuje to co podano jako opcjonalny argument TYTUŁ (można podać dowolny napis) i potem zmierzoną wartość czasu. Sposób wypisania czasu określa opcjonalny argument STYL. Ponieważ argument TYTUŁ jest opcjonalny, można go nie podawać i jeżeli go nie podano, przyjmowany domyślnie jest tekst "timethis COUNT", np. "timethis 100", jeżeli naszym LICZNIK była liczba 100 Liczba ta może być także ujemna, a wtedy oznacza minimalną liczbę sekund procesora, w trakcie których będzie powtarzane wykonanie kodu KOD. Wynikiem funkcji jest obiekt typu Benchmark. Prawidłowe wykonania: timethis( 100, "code" ); timethis( 1e6, sub{ ... } ); timethis( -10, &$code_ref );
timethese
Funkcja uruchamia kilka części kodu ileś razy. Typowe uruchomienie wygląda tak: timethese( LICZNIK, HASZREF, [ STYLE ] ); W takim wywołaniu, LICZNIK oznacza jak poprzednio ilość zadanych wykonań lub czas procesora (gdy jest ujemny) w którym wykonywany będzie kod. Element HASZREF jest referencją do hasza, który w swoich kluczach (klucze mogą być dowolne) trzyma referencje do kodu (najczęściej są to odwołania do kodu lub procedury anonimowe, mogą to także być napisy, które będą wtedy wykonywane przez eval). Dla każdej pary KLUCZ,WARTOŚĆ, zostanie wykonane timethis(LICZNIK,VALUE,KEY,STYLE). Kolejność wykonań tych funkcji jest określona według alfabetycznej kolejności kluczy w HASZREF. Wynikiem funkcji jest obiekt typu Benchmark.
timeit
Sposób wywołania: timeit(LICZNIK, KOD); Licznik jest liczbą oznaczającą ilość powtórzeń kodu do wielokrotnego wykonania, a KOD jest kodem do uruchomienia. Najczęściej jest to referencja do kodu (czyli wartość skalarna). Może to być także napis, który będzie wykonywany przez eval. Tak czy inaczej, wykonanie odbywać się będzie domyślnie w pakiecie w którym umieszczono wywołanie timeit. Sama funkcja zwraca obiekt typu Benchmark.
timediff
Sposób wywołania: timediff( $T1, $T2 );Funkcja oblicza różnicę czasu pomiędzy dwoma wartościami czasowymi, uzyskanymi z pomiarów. Skalary $T1 i $T2 powinny być w tym przypadku obiektami typu Benchmark. Zwracany jest obiekt typu Benchmark, odpowiedni do przekazania do funkcji timestr.
timestr
timestr( TIMEDIFF, [ STYL, [ FORMAT ]] );Funkcja zwraca napis który formatuje czas w obiekcie TIMEDIFF zgodnie z podanym stylem STYL. Argument TIMEDIFF powinien być obiektem podobnym do tego, który zwraca funkcja timediff. Styl może być dowolnym napisem z grupy 'all', 'none', 'noc', 'nop', lub 'auto'. Wartość 'all' pozwala zobaczyć każdy z pięciu dostępnych typów czasu, to znaczy czas rzeczywisty (wallclock time), czas użytkownika, czas systemu, czas procesów potomnych użytkownika oraz czas procesów potomnych systemu. Styl 'noc' pokazuje wszystkie czasy z wyjątkiem czasów procesów potomnych (no-children). Styl 'nop' pokazuje czas rzeczywisty i czasy procesów potomnych. Styl 'auto' (używany domyślnie) pokazuje wszystkie czasy podobnie jak 'all', z tym, że jeżeli czasy procesów potomnych są równe zero, działa podobnie jak 'noc' (nie pokazuje ich). Styl 'none' powoduje że nic nie jest pokazywane (zwracany jest pusty napis). FORMAT jest formatem podobnym trochę do tego używanego w printf, z tym, że bez wiodącego znaku %. Domyślnie format ten ma postać 5.2f.

Interfejs strukturalny: import na żądanie

W module Benchmark znajdują się jeszcze inne funkcje i procedury należące do jego strukturalnego interfejsu, które jednakże powinny być importowane na żądanie, gdyż ich import automatyczny nie następuje. Niektóre z nich są opisane poniżej. Import na żądanie oznacza, że moduł w pragmie use należy ładować np. tak: use Benchmark qw(cmpthese countit) W przeciwnym wypadku funkcje te będą dostępne tylko po pełnej kwalifikacji nazwy, czyli jako Benchmark::cmpthese i Benchmark::countit.

cmpthese
Funkcja wypisuje wynik porównania czasów wielokrotnych wykonań jakichś części kodu, czyli porównuje czasy, jakie można zmierzyć za pomocą timethese. Sposób wywołania: cmpthese( LICZNIK, KODHASZREF, [STYL] ); cmpthese( RESULTHASZREF, [STYL] ); Funkcja opcjonalnie woła timethese, a potem generuje tabelę porównawczą: cmpthese( -1, { inkrementacja => "++\$i", mnożenie => "\$i *= 2" } ); Wynikiem jest tabela podobna do poniższej: Rate mnożenie inkrementacja mnożenie 2831802/s -- -61% inkrementacja 7208959/s 155% -- Tabela jest posortowana od najwolniejszych, do najszybszych i pokazuje różnicę procentową w szybkości pomiędzy każdą parą testów. Funkcja cmpthese może być wraz ze strukturą, którą zwraca jako wynik funkcja timethese: $results = timethese( -1, { inkrementacja=>"++\$i", mnożenie=>"\$i*=2" }); cmpthese($results); w przypadku, gdybyś chciał zobaczyć oba zestawy rezultatów. Wynikiem działania funkcji jest referencja do tablicy wierszy, w której każdy wiersz jest tablicą komórek z powyższej tablicy wyników, włączając w to opisy tekstowe. Zatem: my $rows = cmpthese( -1, { inkrementacja => "++\$i", mnożenie => "\$i *= 2" } ); zwróci strukturę danych podobną do: [ [ '', 'Rate', 'mnożenie', 'inkrementacja' ], [ 'mnożenie', '2831802/s', '--', '-61%' ], [ 'inkrementacja', '7208959/s', '155%', '--' ], ]
countit
Funkcja mierzy ile razy jakiś kawałek kodu został wykonany w określonym czasie. Sposób wykonania: countit( CZAS, KOD );Argument CZAS określa minimalną długość czasu jaka ma być poświęcona na uruchamianie kodu KOD. Argument KOD jest kodem do uruchomienia. Może to być zarówno referencja do kodu, jak i napis będący kodem. W tym drugim przypadku zostanie on uruchomiony za pomocą eval. Każdorazowo uruchomienie kodu odbywa się w tym samym pakiecie, co funkcja countit. Czas NIE jest ujemny. Funkcja może uruchomić kod podany w KOD wiele razy, aby wyliczyć jego prędkość, zanim uruchomi go przez podany CZAS. Funkcja zwraca obiekt typu Benchmark.

Dodatkowe informacje

Należy pamiętać, że czasy mierzone przez moduł Benchmark mogą zmieniać się od uruchomienia do uruchomienia. Dlatego warto wykonać pomiary co najmniej kilkukrotnie, mając na uwadze bieżące obciążenie maszyny. W przypadkach, gdy czas testów jest zbyt krótki, należy zwiększyć liczbę powtórzeń dla wszystkich testowanych kodów.

Pomiar czasu jest wykonywany przez time oraz przez times. Jeżeli w systemie zainstalowano moduł Time::HiRes, można czas mierzyć ze zwiększoną precyzją, dodając do pragmy ładującej moduł dodatkowy element: use Benchmark qw(:hireswallclock); Spowoduje to zmianę precyzji pomiarów z sekund na mikrosekundy. Jeżeli modułu Time::HiRes nie ma w systemie, tag :hireswallclock zostanie po cichu zignorowany. Należy też pamiętać, że porównanie czasów wykonania zawsze odbywa się w czasie procesora, nie w czasie rzeczywistym.

Pozostałe szczegóły są opisane w dokumentacji modułu, można je wyświetlić wpisując polecenie perldoc Benchmark lub man Benchmark. Podobnym źródłem informacji może być także macierzysta strona dokumentacji perlowej, np. http://perldoc.perl.org/Benchmark.html