Perché Raku funziona così male con array multidimensionali?


10

Sono curioso di sapere perché Raku esegue così male manipolando array multidimensionali. Ho fatto un test rapido inizializzando una matrice di 2 dimensioni in Python, C # e Raku e il tempo trascorso è sorprendentemente alto per i successivi.

Per Raku

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

Per 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

Sto sbagliando? Sembra troppa differenza.


5
È lento solo per gli array multidimensionali sagomati (per esempio uno in cui lo si definisce @grid[4000;4000]) il codice Python non utilizza un array sagomato e di voi si prova lo stesso in Raku si ottiene un tempo molto migliore: my @grid = [[0 xx 4000] xx 4000]; significa che è necessario accedere con @grid[0][0]no @grid[0;0]. Penso che ciò sia dovuto principalmente al fatto che gli array sagomati sono ancora in fase di elaborazione.
Scimon Proctor,

1
Sulla mia macchina ci sono @grid[1000;1000] = [[0 xx 1000]xx1000]voluti 12 secondi. @grid = [[0 xx 1000]xx1000]preso 0.6 quindi ... sì. Eviterei array sagomati.
Scimon Proctor,

5
@Scimon è ancora possibile utilizzare l'accessorio [;] per array non modellati. my @grid = [[$++ xx 100] xx 100]; say @grid[0;1]; say @grid[1;1]restituisce rispettivamente 1 e 101
user0721090601

Eccezionale! Questo rende le cose più facili.
Scimon Proctor,

2
Le matrici multidimensionali sagomate non hanno ancora ricevuto la bontà di ottimizzazione che hanno ricevuto molte altre aree di Rakudo.
Elizabeth Mattijsen il

Risposte:


13

Un confronto diretto iniziale

Inizierò con un codice che è molto più strettamente allineato con il tuo codice Python rispetto alla tua traduzione. Penso che il codice Raku più direttamente equivalente al tuo Python sia:

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

Questo codice dichiara un identificatore senza sigillo 1 . It:

  • Gocce "modellanti" ( [4000;4000]in my @table[4000;4000]). L'ho lasciato cadere perché il tuo codice Python non lo sta facendo. La modellatura conferisce vantaggi ma ha implicazioni sulle prestazioni. 2

  • Utilizza l' associazione anziché l' assegnazione . Sono passato all'associazione perché il tuo codice Python sta eseguendo l'associazione, non l'assegnazione. (Python non distingue tra i due.) Mentre l'approccio di assegnazione di Raku porta vantaggi fondamentali che vale la pena avere per il codice generale, ha implicazioni sulle prestazioni. 3


Questo codice con cui ho iniziato la mia risposta è ancora lento.

Innanzitutto, il codice Raku, eseguito tramite un compilatore Rakudo da dicembre 2018, è circa 5 volte più lento del tuo codice Python, utilizzando un interprete Python di giugno 2019, sullo stesso hardware. 3

In secondo luogo, sia il codice Raku che il codice Python sono lenti, ad esempio rispetto al codice C #. Possiamo fare di meglio ...

Un'alternativa idiomatica mille volte più veloce

Vale la pena considerare il seguente codice:

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

Nonostante questo codice corrisponda a una matrice nozionale di 10 miliardi di elementi piuttosto che a soli 16 milioni di elementi uno nel codice Python e C #, il tempo di wallclock per eseguirlo è meno della metà di quello del codice Python e solo 5 volte più lento del C # codice. Ciò suggerisce che Rakudo sta eseguendo il codice Raku più di mille volte più velocemente dell'equivalente codice Python e cento volte più veloce del codice C #.

Il codice Raku sembra essere molto più veloce perché la tabella viene inizializzata pigramente usando xx Inf. 4 L'unico lavoro significativo è svolto sulla gestione della saylinea. Ciò provoca la creazione di 100.000 array di prima dimensione e quindi il popolamento dell'array di dimensioni di 100.000 seconde con 100.000 elementi, in modo tale che sia saypossibile visualizzare il 0trattenuto nell'ultimo elemento dell'array.

C'è più di un modo per farlo

Un problema alla base della tua domanda è che c'è sempre più di un modo per farlo. 5 Se si riscontrano scarse prestazioni per il codice in cui la velocità è critica, codificarla in modo diverso, come ho fatto, può fare una differenza drammatica. 6

(Un'altra opzione davvero buona è quella di porre una domanda SO ...)

Il futuro

Raku è stata attentamente progettata per essere altamente Optimiz grado , vale a dire in grado di uno giorno correre molto più veloce dato lavoro compilatore sufficiente nei prossimi anni , rispetto, ad esempio, Perl 5 o Python 3 può, in teoria, mai eseguito, a meno che non passano attraverso una FALDA riprogettazione e anni di lavoro del compilatore corrispondenti.

Un'analogia in qualche modo OK è ciò che è accaduto con le prestazioni di Java negli ultimi 25 anni. Rakudo / NQP / MoarVM sono circa a metà del processo di maturazione che ha attraversato lo stack Java.

Le note

1 avrei potuto scrivere my $table := .... Ma le dichiarazioni del modulo my \foo ...eliminano la considerazione dei sigilli e ne consentono l'uso =anziché quello :=che sarebbe richiesto con un identificatore sigillo. (Come bonus, "eliminare il sigillo" si traduce in un identificatore privo di sigillo, familiare ai programmatori in molte lingue che non usano sigilli che ovviamente includono Python e C #.)

2 La sagomatura può un giorno comportare operazioni di array più veloci per alcuni codici. Nel frattempo, come indicato nei commenti sulla tua domanda, attualmente chiaramente fa il contrario, rallentandolo in modo significativo. Immagino che ciò sia in gran parte dovuto al fatto che ogni accesso all'array è attualmente ingenuamente controllato dinamicamente , lentamente tutto diminuisce e non c'è stato nessuno sforzo per utilizzare la dimensione fissa per accelerare le cose. Inoltre, quando ho provato a trovare una soluzione rapida per il codice, non sono riuscito a trovarne uno che utilizzava array di dimensioni fisse a causa di molte operazioni su array di dimensioni fisse che al momento non sono state implementate. Ancora una volta, si spera che questi saranno implementati un giorno, ma presumibilmente non sono stati un punto di dolore sufficiente per chiunque a lavorare fino ad oggi.

3 Al momento della stesura di questo, TIO utilizza Python 3.7.4, da giugno 2019 e Rakudo v2018.12, da dicembre 2018. Le prestazioni di Rakudo stanno attualmente migliorando nel tempo in modo significativamente più veloce dell'interprete ufficiale di Python 3, quindi vorrei si aspettano che il divario tra l'ultimo Rakudo e l'ultimo Python, quando Rakudo è più lento, sarà significativamente più stretto di quanto dichiarato in questa risposta. In particolare, il lavoro attuale sta migliorando significativamente le prestazioni degli incarichi.

4 per xx impostazione predefinita è elaborazione lenta, ma alcune espressioni impongono una valutazione avida a causa della semantica del linguaggio o delle attuali limitazioni del compilatore. Nel vecchio anno v2018.12 Rakudo, affinché un'espressione del modulo [ [ foo xx bar ] xx baz ]rimanga pigra e non sia costretta a valutare con entusiasmo, entrambi bar e bazdevono esserlo Inf. Al contrario, my \table = [0 xx 100_000 for ^100_000]è pigro senza l'uso di Inf. (Quest'ultimo codice in realtà memorizza 100.000 Seqs nella prima dimensione anziché 100.000 Arrays - say WHAT table[0]visualizza Seqpiuttosto che Array- ma la maggior parte del codice non sarà in grado di individuare la differenza - say table[99_999;99_999]verrà comunque visualizzata 0.)

5 Alcune persone pensano che sia una debolezza accettare che esiste più di un modo di pensare e codificare soluzioni a determinati problemi. In realtà è un punto di forza in almeno tre aspetti. Innanzitutto, in generale, ogni dato problema non banale può essere risolto da molti algoritmi distinti con differenze drammatiche nel profilo delle prestazioni. Questa risposta include un approccio già disponibile con un Rakudo di un anno che sarà in pratica più di mille volte più veloce di Python in alcuni scenari. In secondo luogo, un linguaggio volutamente flessibile e multi-paradigma di come Raku permette un codificatore (o gruppo di programmatori) per esprimere una soluzione che essi considerano elegante e mantenibile, o che appena ottiene il lavoro fatto, in base a ciò chepensare è meglio, non ciò che la lingua impone. In terzo luogo, le prestazioni di Rakudo come compilatore di ottimizzazione sono attualmente notevolmente variabili. Fortunatamente ha un ottimo profiler 6 , quindi si può vedere dove si trova un collo di bottiglia e una grande flessibilità, quindi si può provare la codifica alternativa e questo può produrre un codice molto più veloce.

6 Se le prestazioni sono importanti o se si stanno esaminando problemi relativi alle prestazioni, consultare la pagina del documento Raku sulle prestazioni ; la pagina copre una gamma di opzioni incluso l'uso del profiler Rakudo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.