web-dev-qa-db-fra.com

Pourquoi Raku fonctionne-t-il si mal avec des tableaux multidimensionnels?

Je suis curieux de savoir pourquoi Raku exécute si mal les tableaux multidimensionnels de manipulation. J'ai fait un test rapide en initialisant une matrice à 2 dimensions en Python, C # et Raku et le temps écoulé est étonnamment élevé pour la dernière.

Pour Raku

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

Pour 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

Suis-je mal? Cela semble beaucoup trop de différence.

10
Javi

Une première comparaison directe

Je vais commencer par un code qui est beaucoup plus étroitement aligné avec votre Python que votre propre traduction. Je pense le code Raku qui est le plus directement équivalent à votre Python est:

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

Ce code déclare un identifiant sans sceau1. Il:

  • Gouttes "mise en forme" (le [4000;4000] dans my @table[4000;4000]). Je l'ai laissé tomber parce que votre code Python ne le fait pas. La mise en forme confère des avantages mais a des implications en termes de performances.2

  • Utilise ( liaison au lieu de affectation . Je suis passé à la liaison parce que votre Python fait la liaison, pas l'affectation. (Python ne fait pas de distinction entre les deux.) Bien que l'approche d'affectation de Raku apporte des avantages fondamentaux qui valent la peine d'avoir pour le code général, cela a des implications sur les performances.3


Ce code avec lequel j'ai commencé ma réponse est encore lent.

Tout d'abord, le code Raku, exécuté via un compilateur Rakudo à partir de décembre 2018, est environ 5 fois plus lent que votre code Python, en utilisant un interpréteur Python à partir de juin 2019). , sur le même matériel.3

Deuxièmement, le code Raku et le code Python sont lents, par exemple par rapport à votre code C #. Nous pouvons faire mieux ...

Une alternative idiomatique mille fois plus rapide

Le code suivant mérite d'être considéré:

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

Malgré ce code correspondant à un tableau d'éléments notionnels 10 milliards plutôt que le simple élément 16 millions un dans votre code Python et C #, le temps d'horloge pour son exécution est inférieur de moitié à celui du code Python, et seulement 5 fois) plus lent que le code C #. Cela suggère que Rakudo exécute le code Raku plus de mille fois plus vite que son équivalent Python, et cent fois plus vite que le code C #.

Le code Raku semble être beaucoup plus rapide car la table est initialisée paresseusement en utilisant xx Inf.4 Le seul travail important est effectué sur l'exécution de la ligne say. Cela provoque la création de 100 000 tableaux de première dimension, puis le remplissage du 100 000e tableau de deuxième dimension avec 100 000 éléments, afin que say puisse afficher le 0 contenue dans le dernier élément de ce tableau.

Il y a plus d'une façon de le faire

Un problème sous-jacent à votre question est qu'il y a toujours plus d'une façon de le faire.5 Si vous rencontrez de mauvaises performances pour le code où la vitesse est critique, le coder différemment, comme je l'ai fait, peut faire une différence dramatique.6

(Une autre très bonne option est de poser une SO question ...)

L'avenir

Raku est soigneusement conçu pour être hautement optimisé capable , c'est-à-dire capable à un jour fonctionner beaucoup plus rapidement étant donné un travail de compilation suffisant au cours des prochaines années , que, disons, Perl 5 ou Python 3 peut, en théorie, jamais fonctionner, à moins qu'ils ne subissent une refonte et des années de travail de compilation correspondant.

Une analogie quelque peu correcte est ce qui s'est passé avec les performances de Java au cours des 25 dernières années. Rakudo/NQP/MoarVM sont à mi-chemin du processus de maturation que la pile Java a traversé.

Notes de bas de page

1 J'aurais pu écrire my $table := .... Mais les déclarations de la forme my \foo ... éliminer la prise en compte des sceaux et autoriser l'utilisation de = plutôt que := qui serait requis avec un identifiant signé. (En bonus, "réduire le sigil" donne un identifiant sans sigil, familier aux codeurs dans les nombreuses langues qui n'utilisent pas de sigils, ce qui inclut bien sûr Python et C #.))

2 La mise en forme peut un jour entraîner des opérations de tableau plus rapides pour certains codes. En attendant, comme mentionné dans les commentaires sur votre question, cela fait clairement le contraire actuellement, ce qui la ralentit considérablement. J'imagine que c'est en grande partie parce que chaque accès au tableau est naïvement dynamiquement vérifié pour le moment, lentement tout en bas, et il n'y a pas eu non plus d'efforts pour utiliser la taille fixe pour aider accélérer les choses. De plus, lorsque j'ai essayé de trouver une solution de contournement rapide pour votre code, je n'ai pas réussi à en trouver un en utilisant le tableau de taille fixe car de nombreuses opérations sur les tableaux de taille fixe n'étaient actuellement pas implémentées. Encore une fois, il est à espérer qu'elles seront mises en œuvre un jour, mais elles n'ont probablement pas été un point de souffrance suffisant pour quiconque de travailler à leur mise en œuvre à ce jour.

3 Au moment d'écrire ces lignes, TIO utilise Python 3.7.4, à partir de juin 2019 et Rakudo v2018.12 , à partir de décembre 2018. Les performances de Rakudo s'améliorent actuellement au fil du temps beaucoup plus rapidement que l'interprète officiel Python 3 est, donc je m'attendrais à l'écart entre le dernier Rakudo et le dernier Python, lorsque Rakudo est plus lent , pour être significativement plus étroit que ce qui est indiqué dans cette réponse. En particulier, le travail actuel améliore considérablement les performances des missions.

4 xx utilise par défaut un traitement paresseux mais certaines expressions forcent une évaluation impatiente en raison de la sémantique du langage ou des limitations actuelles du compilateur. Dans le Rakudo v2018.12 de l'année, pour une expression de la forme [ [ foo xx bar ] xx baz ] pour rester paresseux et ne pas être obligé d'évaluer avec impatience, les deux bar et baz doivent être Inf. En revanche, my \table = [0 xx 100_000 for ^100_000] est paresseux sans utiliser Inf. (Ce dernier code stocke en réalité 100 000 Seqs dans la première dimension plutôt que 100 000 Arrays - say WHAT table[0] affiche Seq plutôt que Array - mais la plupart du code ne sera pas en mesure de détecter la différence - say table[99_999;99_999] affichera toujours 0.)

5 Certains pensent que c'est une faiblesse d'accepter qu'il y a plus d'une façon de penser et de coder des solutions à des problèmes donnés. En réalité, c'est une force à au moins trois égards. Premièrement, en général, tout problème donné non trivial peut être résolu par de nombreux algorithmes distincts avec des différences dramatiques dans le profil de performance. Cette réponse inclut une approche déjà disponible avec un Rakudo d'un an qui sera plus de mille fois plus rapide que Python dans la pratique dans certains scénarios. Deuxièmement, un langage délibérément flexible et multi-paradigme comme Raku permet à un codeur (ou à une équipe de codeurs) d'exprimer une solution qu'ils qu'ils considèrent élégante et maintenable, ou qui fait juste le travail, en fonction de ce que ils pensent que c'est mieux, pas ce que le langage impose. Troisièmement, les performances de Rakudo en tant que compilateur d'optimisation sont actuellement considérablement variables. Heureusement, il a un excellent profileur6, donc on peut voir où se trouve un goulot d'étranglement, et une grande flexibilité, donc on peut essayer un codage alternatif et cela peut produire un code beaucoup plus rapide.

6 Lorsque les performances sont importantes, ou si vous étudiez des problèmes de performances, consultez la page doc Raku sur les performances ; la page couvre une gamme d'options, y compris l'utilisation du profileur Rakudo.

12
raiph