Warum ist Raku mit mehrdimensionalen Arrays so schlecht?


10

Ich bin neugierig, warum Raku so schlechte Manipulationen an mehrdimensionalen Arrays durchführt. Ich habe einen kurzen Test durchgeführt, um eine zweidimensionale Matrix in Python, C # und Raku zu initialisieren, und die verstrichene Zeit ist für später überraschend hoch.

Für Raku

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

Für Python

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

C #

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

Mache ich falsch Es scheint viel zu viel Unterschied.


5
Es ist nur langsam für geformte mehrdimensionale Arrays (z. B. eines, bei dem Sie es definieren @grid[4000;4000]). Der Python-Code verwendet kein geformtes Array, und wenn Sie dasselbe in Raku versuchen, erhalten Sie eine viel bessere Zeit zurück: my @grid = [[0 xx 4000] xx 4000]; Es bedeutet, dass Sie mit zugreifen müssen @grid[0][0]nicht @grid[0;0]. Ich denke, das liegt hauptsächlich daran, dass geformte Arrays noch in Arbeit sind.
Scimon Proctor

1
Auf meinem Computer @grid[1000;1000] = [[0 xx 1000]xx1000]dauerte es 12 Sekunden. @grid = [[0 xx 1000]xx1000]nahm 0,6 also ... ja. Ich würde geformte Arrays vermeiden.
Scimon Proctor

5
@Scimon Sie können den Accessor [;] auch weiterhin für ungeformte Arrays verwenden. my @grid = [[$++ xx 100] xx 100]; say @grid[0;1]; say @grid[1;1]gibt 1 bzw. 101 zurück
user0721090601

Genial! Das macht die Sache einfacher.
Scimon Proctor

2
Geformte mehrdimensionale Arrays haben noch nicht die Optimierungsgüte erhalten, die viele andere Bereiche von Rakudo erhalten haben.
Elizabeth Mattijsen

Antworten:


13

Ein erster direkter Vergleich

Ich beginne mit Code, der viel enger mit Ihrem Python-Code übereinstimmt als mit Ihrer eigenen Übersetzung. Ich denke, der Raku-Code, der Ihrem Python am direktesten entspricht, ist:

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

Dieser Code deklariert eine Siegel-freie Kennung 1 . Es:

  • Tropfen "Shaping" (das [4000;4000]In my @table[4000;4000]). Ich habe es fallen lassen, weil Ihr Python-Code es nicht tut. Das Formen bietet Vorteile, hat jedoch Auswirkungen auf die Leistung. 2

  • Verwendet Bindung anstelle von Zuweisung . Ich habe auf Bindung umgestellt, weil Ihr Python-Code eine Bindung und keine Zuweisung vornimmt. (Python unterscheidet nicht zwischen beiden.) Während Rakus Zuweisungsansatz grundlegende Vorteile bietet, die sich für allgemeinen Code lohnen, hat er Auswirkungen auf die Leistung. 3


Dieser Code, mit dem ich meine Antwort begonnen habe, ist immer noch langsam.

Erstens ist der Raku-Code, der ab Dezember 2018 über einen Rakudo-Compiler ausgeführt wird, mit einem Python-Interpreter ab Juni 2019 auf derselben Hardware etwa fünfmal langsamer als Ihr Python-Code. 3

Zweitens sind sowohl der Raku-Code als auch der Python-Code langsam, z. B. im Vergleich zu Ihrem C # -Code. Wir können es besser machen ...

Eine idiomatische Alternative, die tausendmal schneller ist

Der folgende Code ist eine Überlegung wert:

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

Obwohl dieser Code einem fiktiven 10-Milliarden- Element-Array entspricht und nicht nur 16 Millionen Elementen in Ihrem Python- und C # -Code, ist die Wanduhrzeit für die Ausführung weniger als die Hälfte der des Python-Codes und nur fünfmal langsamer als die des C # -Codes Code. Dies deutet darauf hin, dass Rakudo den Raku-Code mehr als tausendmal so schnell wie den entsprechenden Python-Code und hundertmal so schnell wie der C # -Code ausführt.

Der Raku-Code scheint so viel schneller zu sein, weil die Tabelle mithilfe von träge initialisiert wird xx Inf. 4 Die einzige wichtige Arbeit wird beim Betrieb der sayLeitung geleistet . Dies führt dazu, dass 100.000 Arrays der ersten Dimension erstellt werden und dann nur das 100.000ste Array der zweiten Dimension mit 100.000 Elementen gefüllt wird, sodass das im letzten Element dieses Arrays enthaltene sayangezeigt werden kann 0.

Es gibt mehr als einen Weg, dies zu tun

Ein Problem, das Ihrer Frage zugrunde liegt, ist, dass es immer mehr als einen Weg gibt, dies zu tun. 5 Wenn Sie bei Code, bei dem die Geschwindigkeit von entscheidender Bedeutung ist, auf eine schlechte Leistung stoßen, kann eine andere Codierung wie bisher einen dramatischen Unterschied bewirken. 6

(Eine andere wirklich gute Option ist, eine SO-Frage zu stellen ...)

Die Zukunft

Raku wurde sorgfältig entwickelt, um in hohem Maße optimierbar zu sein , dh in der Lage zu sein, eines Tages bei ausreichender Compilerarbeit in den kommenden Jahren viel schneller zu laufen , als beispielsweise Perl 5 oder Python 3 theoretisch jemals ausgeführt werden können, es sei denn, sie durchlaufen einen Grund- Up Redesign und jahrelange entsprechende Compilerarbeit.

Eine etwas gute Analogie ist die Leistung von Java in den letzten 25 Jahren. Rakudo / NQP / MoarVM befinden sich ungefähr in der Mitte des Reifungsprozesses, den der Java-Stack durchlaufen hat.

Fußnoten

1 Ich hätte schreiben können my $table := .... Deklarationen des Formulars my \foo ...eliminieren jedoch die Berücksichtigung von Siegeln und ermöglichen die Verwendung von =anstelle von :=Siegel, die mit einer Siegelkennung erforderlich wären. (Als Bonus führt das "Herausschneiden des Siegels" zu einer Siegel-freien Kennung, die Codierern in den vielen Sprachen, die keine Siegel verwenden, vertraut ist, zu denen natürlich Python und C # gehören.)

2 Die Formgebung kann eines Tages zu schnelleren Array-Operationen für einige Codes führen. In der Zwischenzeit macht es, wie in den Kommentaren zu Ihrer Frage erwähnt, eindeutig das Gegenteil und verlangsamt es erheblich. Ich stelle mir vor, dass dies zu einem großen Teil darauf zurückzuführen ist, dass jeder Array-Zugriff im Moment naiv dynamisch überprüft wird, langsam alles heruntergefahren wird und es auch keine Anstrengungen gab, die feste Größe zu verwenden, um die Dinge zu beschleunigen. Als ich versuchte, eine schnelle Problemumgehung für Ihren Code zu finden, konnte ich keine finden, die das Array mit fester Größe verwendet, da viele Vorgänge mit Arrays mit fester Größe derzeit nicht implementiert sind. Auch diese werden hoffentlich eines Tages implementiert, waren aber vermutlich kein ausreichender Schmerzpunkt, um an ihrer Implementierung zu arbeiten.

3 Zum Zeitpunkt des Schreibens dieses Artikels verwendet TIO Python 3.7.4 ab Juni 2019 und Rakudo v2018.12 ab Dezember 2018. Die Leistung von Rakudo verbessert sich derzeit im Laufe der Zeit erheblich schneller als der offizielle Python 3-Interpreter Erwarten Sie, dass die Lücke zwischen dem neuesten Rakudo und dem neuesten Python, wenn Rakudo langsamer ist, erheblich enger ist als in dieser Antwort angegeben. Insbesondere die aktuelle Arbeit verbessert die Leistung von Aufgaben erheblich.

4 verwendet xx standardmäßig eine verzögerte Verarbeitung, aber einige Ausdrücke erzwingen aufgrund der Sprachsemantik oder der aktuellen Einschränkungen des Compilers eine eifrige Auswertung. Im Jahr alten v2018.12 Rakudo, damit ein Ausdruck der Form [ [ foo xx bar ] xx baz ]faul bleibt und nicht gezwungen wird, eifrig zu bewerten, beides bar und bazmuss sein Inf. Im Gegensatz dazu my \table = [0 xx 100_000 for ^100_000]ist faul ohne Verwendung von Inf. (Der letztere Code speichert tatsächlich 100.000 Seqs in der ersten Dimension anstatt 100.000 Arrays - say WHAT table[0]Anzeigen Seqstatt Array- aber der meiste Code kann den Unterschied nicht erkennen - say table[99_999;99_999]wird weiterhin angezeigt 0.)

5 Einige Leute halten es für eine Schwäche zu akzeptieren, dass es mehr als eine Möglichkeit gibt, über Lösungen für bestimmte Probleme nachzudenken und diese zu codieren. In Wirklichkeit ist es eine Stärke in mindestens drei Punkten. Erstens kann im Allgemeinen jedes gegebene nicht triviale Problem durch viele unterschiedliche Algorithmen mit dramatischen Unterschieden im Leistungsprofil gelöst werden. Diese Antwort enthält einen Ansatz, der bereits mit einem einjährigen Rakudo verfügbar ist und in einigen Szenarien in der Praxis mehr als tausendmal schneller als Python ist. Zweitens ermöglicht eine bewusst flexible und paradigmenübergreifende Sprache wie Raku einem Codierer (oder einem Team von Codierern), eine Lösung auszudrücken, die sie als elegant und wartbar betrachten oder die nur die Arbeit erledigt, basierend auf dem, was sie tunDenken ist am besten, nicht was die Sprache auferlegt. Drittens ist Rakudos Leistung als optimierender Compiler derzeit besonders unterschiedlich. Glücklicherweise hat es einen großartigen Profiler 6 , so dass man sehen kann, wo ein Engpass liegt, und eine große Flexibilität, so dass man eine alternative Codierung ausprobieren kann, was zu einem erheblich schnelleren Code führen kann.

6 Wenn es um Leistung geht oder wenn Sie Leistungsprobleme untersuchen, lesen Sie die Raku-Dokumentseite zur Leistung . Die Seite behandelt eine Reihe von Optionen, einschließlich der Verwendung des Rakudo-Profilers.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.