Dlaczego Raku tak źle radzi sobie z tablicami wielowymiarowymi?

10

Ciekawe, dlaczego Raku tak źle manipuluje wielowymiarowymi tablicami. Zrobiłem szybki test inicjujący macierz 2-wymiarową w Pythonie, C # i Raku, a upływający czas jest zaskakująco długi jak na później.

Dla Raku

my @grid[4000;4000] = [[0 xx 4000] xx 4000];
# Elapsed time 42 seconds !!

Dla Pythona

table= [ [ 0 for i in range(4000) ] for j in range(4000) ]
# Elapsed time 0.51 seconds

DO#

int [,]matrix = new int[4000,4000];
//Just for mimic same behaviour
for(int i=0;i<4000;i++)
   for(int j=0;j<4000;j++)
       matrix[i,j] = 0;
# Elapsed time 0.096 seconds

Czy robię źle? Wydaje się, że to zbyt duża różnica.

Javi
źródło
5
Jest on powolny tylko w przypadku kształtowanych wielowymiarowych tablic (np. W tym, w którym go definiujesz @grid[4000;4000]), kod python nie używa tablic w kształcie, a jeśli spróbujesz tego samego w Raku, otrzymasz znacznie lepszy czas: my @grid = [[0 xx 4000] xx 4000]; Oznacza to, że musisz uzyskać dostęp za pomocą @grid[0][0]nie @grid[0;0]. Myślę, że dzieje się tak głównie dlatego, że tablice kształtowe są wciąż w toku.
Scimon Proctor,
1
Na mojej maszynie @grid[1000;1000] = [[0 xx 1000]xx1000]zajęło 12 sekund. @grid = [[0 xx 1000]xx1000]wziął 0,6, więc ... tak. Unikałbym tablic kształtowych.
Scimon Proctor,
5
@ Scimon nadal możesz używać akcesorium [;] dla nieukształtowanych tablic. my @grid = [[$++ xx 100] xx 100]; say @grid[0;1]; say @grid[1;1]zwraca odpowiednio 1 i 101
user0721090601
Niesamowite! To ułatwia rzeczy.
Scimon Proctor
2
Ukształtowane wielowymiarowe tablice nie otrzymały jeszcze dobroci optymalizacji, jak wiele innych obszarów Rakudo.
Elizabeth Mattijsen,

Odpowiedzi:

13

Wstępne bezpośrednie porównanie

Zacznę od kodu, który jest znacznie ściślej dopasowany do twojego kodu Python niż twoje własne tłumaczenie. Myślę, że kod Raku, który najbardziej bezpośrednio odpowiada Twojemu Pythonowi:

my \table = [ [ 0 for ^4000 ] for ^4000 ];
say table[3999;3999]; # 0

Ten kod deklaruje identyfikator sigil 1 . To:

  • Krople "kształtowanie" (The [4000;4000]w my @table[4000;4000]). Upuściłem go, ponieważ twój kod w Pythonie tego nie robi. Kształtowanie zapewnia korzyści, ale ma wpływ na wydajność. 2)

  • Używa wiązania zamiast przypisania . Przełączyłem się na wiązanie, ponieważ kod Pythona wykonuje wiązanie, a nie przypisywanie. (Python nie rozróżnia tych dwóch.) Chociaż podejście Raku do przypisywania ma podstawowe zalety, które warto mieć dla ogólnego kodu, ma on wpływ na wydajność. 3)


Ten kod, od którego zacząłem odpowiadać, jest wciąż wolny.

Po pierwsze, kod Raku, uruchamiany za pomocą kompilatora Rakudo od grudnia 2018 r., Jest około 5 razy wolniejszy niż kod Python, przy użyciu interpretera Python od czerwca 2019 r., Na tym samym sprzęcie. 3)

Po drugie, zarówno kod Raku, jak i kod Python są wolne, np. W porównaniu do kodu C #. Możemy zrobić lepiej ...

Idiomatyczna alternatywa, która jest tysiąc razy szybsza

Warto rozważyć następujący kod:

my \table = [ [ 0 xx Inf ] xx Inf ];
say table[ 100_000; 100_000 ]; # 0

Pomimo tego kodu odpowiadającego hipotetycznej tablicy 10 miliardów elementów zamiast zaledwie 16 milionom elementów w kodzie Python i C #, czas działania wallclocka jest mniej niż o połowę krótszy niż w kodzie Python i tylko 5 razy wolniejszy niż C # kod. Sugeruje to, że Rakudo uruchamia kod Raku ponad tysiąc razy szybciej niż równoważny kod Pythona i sto razy szybciej niż kod C #.

Kod Raku wydaje się być o wiele szybszy, ponieważ tabela jest inicjowana leniwie za pomocą xx Inf. 4 Jedyną znaczącą pracę wykonuje się przy uruchomieniu saylinii. Powoduje to utworzenie 100 000 tablic pierwszego wymiaru, a następnie zapełnienie 100 000 elementów drugiej tablicy 100 000 elementów, dzięki czemu saymożna wyświetlić0 wstrzymany ostatni element tej tablicy.

Jest więcej niż jeden sposób, aby to zrobić

Jednym z problemów leżących u podstaw twojego pytania jest to, że zawsze istnieje więcej niż jeden sposób, aby to zrobić. 5 Jeśli napotkasz słabą wydajność kodu, w przypadku którego szybkość ma krytyczne znaczenie, kodowanie go w inny sposób, tak jak ja, może mieć ogromne znaczenie. 6

(Inną naprawdę dobrą opcją jest zadanie pytania SO ...)

Przyszłość

Raku jest starannie zaprojektowany, aby być wysoko optimiz stanie , to znaczy w stanie na jeden dzień działał znacznie szybciej dana wystarczająca praca kompilator w najbliższych latach , niż, powiedzmy, 5 Perl lub Python 3 może teoretycznie kiedykolwiek biegać, chyba że przejść naziemnych do przeprojektowania i lat odpowiedniej pracy kompilatora.

Nieco odpowiednia analogia dotyczy tego, co działo się z wydajnością Javy w ciągu ostatnich 25 lat. Rakudo / NQP / MoarVM są w połowie procesu dojrzewania, przez który przeszedł stos Java.

Przypisy

1 Mógłbym napisać my $table := .... Jednak deklaracje w formularzu my \foo ...eliminują rozpatrywanie pieczęci i pozwalają na użycie, =a nie tego, :=co byłoby wymagane przy użyciu identyfikatora pieczęci. (Jako bonus „wycięcie sigila” skutkuje identyfikatorem wolnym od sigilu, znanym programistom w wielu językach, które nie używają sigili, co oczywiście obejmuje Python i C #.)

2 Kształtowanie może pewnego dnia skutkować szybszymi operacjami tablicowymi dla niektórych kodów. Tymczasem, jak wspomniano w komentarzach do twojego pytania, obecnie wyraźnie robi to odwrotnie, znacznie go spowalniając. Wyobrażam sobie, że jest to w dużej mierze spowodowane tym, że każdy dostęp do tablicy jest obecnie naiwnie dynamicznie sprawdzany pod kątem ograniczeń, powoli wszystko w dół, a także nie starano się używać ustalonego rozmiaru, aby przyspieszyć. Ponadto, kiedy próbowałem wymyślić szybkie obejście dla twojego kodu, nie udało mi się znaleźć takiego, który używa tablicy o stałym rozmiarze, ponieważ wiele operacji na tablicach o stałym rozmiarze jest obecnie niewdrożonych. Ponownie, miejmy nadzieję, zostaną one wdrożone pewnego dnia, ale przypuszczalnie nie były wystarczającym utrudnieniem dla nikogo do pracy nad ich wdrożeniem do tej pory.

3 W chwili pisania tego tekstu TIO używa Pythona 3.7.4 od czerwca 2019 r. I Rakudo v2018.12 od grudnia 2018 r. Wydajność Rakudo poprawia się z biegiem czasu znacznie szybciej niż oficjalny interpreter Pythona 3, więc chciałbym spodziewaj się, że różnica między najnowszym Rakudo a najnowszym Pythonem, gdy Rakudo jest wolniejszy, będzie znacznie węższa niż podano w tej odpowiedzi. W szczególności bieżąca praca znacznie poprawia wykonanie zadań.

4 xx domyślnie leniwe przetwarzanie, ale niektóre wyrażenia wymuszają chętną ocenę ze względu na semantykę języka lub obecne ograniczenia kompilatora. W rocznym v2018.12 Rakudo, aby wyrażenie formularza [ [ foo xx bar ] xx baz ]pozostało leniwe i nie było zmuszane do oceniania z niecierpliwością, jedno bar i drugiebaz musi być Inf. Natomiast my \table = [0 xx 100_000 for ^100_000]jest leniwy bez użycia Inf. (Ten ostatni kod przechowuje 100 000 Seqs w pierwszym wymiarze zamiast 100 000 Arrays - say WHAT table[0]wyświetla Seqzamiast Array- ale większość kodu nie będzie w stanie dostrzec różnicy - say table[99_999;99_999]nadal będzie wyświetlana 0).

5 Niektórzy uważają, że słabością jest zaakceptowanie, że istnieje więcej niż jeden sposób myślenia i kodowania rozwiązań zadanych problemów. W rzeczywistości jest to siła przynajmniej w trzech aspektach. Po pierwsze, ogólnie rzecz biorąc, każdy dany nietrywialny problem można rozwiązać za pomocą wielu różnych algorytmów o dramatycznych różnicach w profilu wydajności. Ta odpowiedź obejmuje podejście już dostępne dla rocznego Rakudo, które w niektórych scenariuszach będzie ponad tysiąc razy szybsze niż Python. Po drugie, celowo elastyczny i wieloparadowy język, taki jak Raku, pozwala koderowi (lub zespołowi programistów) wyrazić rozwiązanie, które uważają za eleganckie i łatwe w utrzymaniu lub które po prostu wykonuje zadanie, w oparciu o to , co robiąmyśl jest najlepsza, a nie to, co narzuca język. Po trzecie, wydajność Rakudo jako kompilatora optymalizującego jest obecnie bardzo zmienna. Na szczęście ma świetny profiler 6 , dzięki czemu można zobaczyć, gdzie jest wąskie gardło, i dużą elastyczność, dzięki czemu można wypróbować alternatywne kodowanie, co może wygenerować znacznie szybszy kod.

6 Gdy liczy się wydajność lub gdy badasz problemy z wydajnością, zajrzyj na stronę dokumentacji Raku na temat wydajności ; strona zawiera szereg opcji, w tym użycie profilera Rakudo.

raiph
źródło