Jak wiecie, do tej pory testowaliśmy nasz kod przy użyciu podzbioru danych, który zawiera tylko pierwsze 100 obserwacji. Jednak, jak widzieliśmy na początku , wydajność może się różnić dla różnych implementacji, w zależności od rozmiaru danych wejściowych. Aby zebrać wszystkie nasze wysiłki w tym rozdziale, utworzymy kilka funkcji, które pomogą nam zmierzyć, jak zmieniają się czasy wykonania naszych implementacji, gdy korzystamy z większej liczby obserwacji z naszych danych. Najpierw wprowadzamy nasze wymagania do R, głównie pakiety microbenchmark i ggplot2 oraz pliki, które zawierają nasze implementacje. Następnie tworzymy funkcję sma_performance(), która przyjmuje symbol, period, original_data, listę nazwaną sizes, której elementy są liczbą obserwacji, które zostaną pobrane z original_data do przetestowania naszych implementacji, cluster aby uniknąć narzutu inicjowania jej w naszej funkcji sma_parallel(), jak widzieliśmy w odpowiedniej sekcji i ile razy chcemy mierzyć każdą implementację. Jak widać, dla każdego rozmiaru w rozmiarach bierzemy odpowiednią liczbę obserwacji w obiekcie data i wysyłamy ją wraz z innymi niezbędnymi argumentami dla funkcji sma_microbenchmark(). Następnie dodajemy wartość size do ramki danych result, która jest zapewniana przez funkcję summary() zastosowaną na wierzchu obiektu wynikowego z funkcji mirobenchmar() z sma_microbenchmark(). Musimy to dodać sami, ponieważ funkcja nie ma żadnej wiedzy na temat rozmiaru danych, z którymi ma do czynienia. Na koniec spłaszczamy listę ramek danych na liście results za pomocą funkcji do.call(„rbind”, results), która wysyła pojedynczą ramkę danych jako dane wyjściowe. Funkcja sma_icrobenchmark() jest bardzo prosta. Otrzymuje tylko niektóre parametry i przekazuje je dalej do każdej implementacji, która będzie mierzona przez funkcję. Zwróć uwagę, że zostawiamy wewnątrz funkcję sma_paralel)inefficient(), ale jest ona zakomentowana, aby uniknąć problemów ze skalą na wykresie, który w końcu utworzymy (ponieważ jest bardzo powolny, wypaczy nasz wykres). Wynikowy obiekt funkcji sma_performance() zwraca ramkę danych z wynikami dla wszystkich testów, która jest używana jako dane wejściowe dla funkcji graph_sma_performance() w postaci obiektów results. Otrzymuje również sizes, który zostanie użyty do zdefiniowania wartości na osi x. Jak widać, wywołujemy remove_arguments(), o czym wspominamy, idąc naprzód. Tworzy wykres przy użyciu funkcji ggplot(), geom_point() i geom_line(), jak widzieliśmy wcześniej, i używamy skal logarytmicznych dla obu osi. Funkcja remove_arguments() robi dokładnie to, co mówi, usuwa nawiasy i argumenty z wywołań funkcji, dzięki czemu zachowujemy tylko nazwę funkcji. Ma to na celu zmniejszenie miejsca w legendzie wykresu. Aby to osiągnąć, używamy funkcji gsub(), którą widzieliśmy wcześniej. Aby użyć przedstawionego kodu, po prostu tworzymy listę sizes, której brakuje i używamy wszystkich innych obiektów, które zdefiniowaliśmy wcześniej. W tym konkretnym przypadku chcemy zmierzyć pierwsze 10, 100, 1000 i 10 000 obserwacji. Jeśli chcesz, możesz zwiększyć tę listę o większe kwoty. Pamiętaj, że całkowita liczba obserwacji w symulowanych danych to nieco ponad 1 000 000. Powstały wykres przedstawia liczbę obserwacji na osi x i medianę w mikrosekundach na osi y. Obie osie używają skali logarytmicznej, więc miej na uwadze interpretację relacji. Jak widać, gdy rozmiar danych wejściowych jest mniejszy (po lewej stronie wykresu), różnica czasu wykonania jest mniejsza, a gdy zwiększamy rozmiar wejściowy, różnice zaczynają być coraz większe, szczególnie biorąc pod uwagę skale logarytmiczne. Oto kilka interesujących rzeczy, na które należy zwrócić uwagę:
- Funkcja sma_efficient_1 okazała się wolniejsza niż sma_slow_7() w przypadku 100 obserwacji, w rzeczywistości jest szybsza przy użyciu 10 000 obserwacji. To pokazuje, że kompromis miał sens, zwłaszcza w miarę wzrostu nakładów.
- sma_efficient_2() : Ta implementacja jest szybsza, dla 10 obserwacji, niż implementacja Fortran. To dość zaskakujące i pokazuje, że narzut związany z wywołaniem kodu w języku Fortran nie jest tego wart przy takim rozmiarze danych wejściowych. Jednak sma_efficient_2() szybko staje się wolniejszy wraz ze wzrostem rozmiaru wejściowego.
- sma_paralle(): Ta implementacja jest powolna z powodu całego narzutu, jaki ponosi, jak widzieliśmy w odpowiedniej sekcji, ale jest to również implementacja, w której procentowy wzrost czasu jest najmniejszy, gdy zwiększa się rozmiar danych wejściowych. To powinno nas skłonić do zastanowienia się, co się dzieje, gdy mamy do czynienia z pełnymi danymi? Czy w tym momencie będzie szybciej niż implementacje Fortran lub C ++, które wydają się rosnąć szybciej? Pozostawiamy to jako ćwiczenie dla czytelnika.
Na koniec, dla ciekawskiego czytelnika, jak myślisz, co się stanie, jeśli użyjesz implementacji sma_delegated_cpp() wraz z podejściem zrównoleglania jakie pokazaliśmy? Jeśli chcesz poznać odpowiedź, zdecydowanie powinieneś spróbować samemu.