Por que Raku tem um desempenho tão ruim com matrizes multidimensionais?


10

Estou curioso para saber por que Raku realiza uma manipulação tão ruim de matrizes multidimensionais. Fiz um teste rápido ao inicializar uma matriz de duas dimensões em Python, C # e Raku, e o tempo decorrido é surpreendentemente alto para o posterior.

Para Raku

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

Para 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

Estou fazendo errado? Parece muita diferença.


5
É lento para matrizes multidimensionais modeladas (EG, onde você a define @grid[4000;4000]), o código python não está usando uma matriz modelada e, se você tentar o mesmo em Raku, obtém um tempo muito melhor: my @grid = [[0 xx 4000] xx 4000]; significa que você precisa acessar com @grid[0][0]não @grid[0;0]. Eu acho que isso ocorre principalmente porque matrizes modeladas ainda são um trabalho em andamento.
Scimon Proctor 04/12/19

11
Na minha máquina @grid[1000;1000] = [[0 xx 1000]xx1000]demorou 12 segundos. @grid = [[0 xx 1000]xx1000]levou 0,6 então ... sim. Eu evitaria matrizes em forma.
Scimon Proctor 04/12/19

5
@ Scimon, você ainda pode usar o acessador [;] para matrizes sem forma. my @grid = [[$++ xx 100] xx 100]; say @grid[0;1]; say @grid[1;1]retorna 1 e 101 respectivamente
user0721090601

Impressionante! Isso facilita as coisas.
Scimon Proctor 04/12/19

2
Matrizes multidimensionais em forma ainda não receberam a vantagem de otimização que muitas outras áreas de Rakudo receberam.
Elizabeth Mattijsen 04/12/19

Respostas:


13

Uma comparação direta inicial

Começarei com um código que está muito mais alinhado com o seu código Python do que com sua própria tradução. Eu acho que o código Raku que é mais diretamente equivalente ao seu Python é:

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

Este código declara um identificador sem sigilo 1 . Isto:

  • Gotas "shaping" (o [4000;4000]no my @table[4000;4000]). Eu o larguei porque seu código Python não está fazendo isso. A modelagem confere vantagens, mas tem implicações no desempenho. 2

  • Usa ligação em vez de atribuição . Eu mudei para a associação porque seu código Python está vinculando, não atribuindo. (O Python não faz distinção entre os dois.) Embora a abordagem de atribuição de Raku traga vantagens fundamentais que valem a pena para o código geral, ela tem implicações no desempenho. 3


Esse código com o qual eu comecei minha resposta ainda é lento.

Primeiro, o código Raku, executado através de um compilador Rakudo a partir de dezembro de 2018, é cerca de 5 vezes mais lento que o código Python, usando um intérprete Python a partir de junho de 2019, no mesmo hardware. 3

Segundo, o código Raku e o código Python são lentos, por exemplo, comparados ao seu código C #. Nós podemos fazer melhor ...

Uma alternativa idiomática mil vezes mais rápida

Vale a pena considerar o seguinte código:

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

Apesar desse código corresponder a uma matriz de 10 bilhões de elementos nocional em vez dos meros 16 milhões de elementos um no código Python e C #, o tempo de execução do relógio de parede é menos da metade do código Python e apenas 5 vezes mais lento que o C # código. Isso sugere que Rakudo está executando o código Raku mais de mil vezes mais rápido que o código Python equivalente e cem vezes mais rápido que o código C #.

O código Raku parece ser muito mais rápido porque a tabela está sendo inicializada lentamente usando xx Inf. 4 O único trabalho significativo é realizado na execução da saylinha. Isso causa a criação de 100.000 matrizes de primeira dimensão e, em seguida, preenche apenas a 100.000ª matriz de segunda dimensão com 100.000 elementos, para que saypossa exibir a 0retenção no último elemento dessa matriz.

Há mais de uma maneira de fazer isso

Um problema subjacente à sua pergunta é que sempre há mais de uma maneira de fazer isso. 5 Se você encontrar um desempenho ruim do código em que a velocidade é crítica, codificá-lo de maneira diferente, como eu fiz, pode fazer uma diferença drástica. 6

(Outra opção realmente boa é fazer uma pergunta SO ...)

O futuro

Raku é cuidadosamente projetado para ser altamente optimiz capaz , ou seja, capaz de um dia correr muito mais rápido dado trabalho compilador suficiente nos próximos anos , do que, digamos, Perl 5 ou Python 3 pode, em teoria, nunca correr, a menos que eles passam por um subterrânea redesenho e anos de trabalho correspondente do compilador.

Uma analogia um tanto aceitável foi o que aconteceu com o desempenho do Java nos últimos 25 anos. Rakudo / NQP / MoarVM estão na metade do processo de amadurecimento pelo qual a pilha Java passou.

Notas de rodapé

1 eu poderia ter escrito my $table := .... Mas declarações de forma my \foo ...a eliminar a consideração de sigilos e permitir o uso de =mais do que :=o que seria necessário com um identificador sigil'd. (Como um bônus, "cortar o sigilo" resulta em um identificador livre de sigilo, familiar aos codificadores nas diversas linguagens que não usam sigilos, que obviamente incluem Python e C #.)

2 A modelagem pode um dia resultar em operações de matriz mais rápidas para algum código. Enquanto isso, como mencionado nos comentários da sua pergunta, ele claramente faz o oposto atualmente, diminuindo significativamente a velocidade. Imagino que é em grande parte porque cada acesso a matriz está sendo ingenuamente dinamicamente limites verificado no momento, lentamente tudo para baixo, e também houve nenhum esforço para utilizar o tamanho fixo de coisas ajudar a acelerar. Além disso, quando tentei criar uma solução rápida para o seu código, não encontrei uma usando a matriz de tamanho fixo devido a muitas operações em matrizes de tamanho fixo serem atualmente não implementadas. Mais uma vez, esperamos que eles sejam implementados um dia, mas presumivelmente não foram um ponto de dor suficiente para alguém trabalhar na implementação deles até o momento.

3 No momento em que escrevi isso, o TIO estava usando o Python 3.7.4, de junho de 2019, e o Rakudo v2018.12, de dezembro de 2018. Atualmente, o desempenho do Rakudo está melhorando ao longo do tempo significativamente mais rápido do que o intérprete oficial do Python 3, então eu faria esperamos que a diferença entre o Rakudo mais recente e o Python mais recente, quando o Rakudo for mais lento, seja significativamente menor do que o indicado nesta resposta. Em particular, o trabalho atual está melhorando significativamente o desempenho das atribuições.

O xx padrão 4 é o processamento lento, mas algumas expressões forçam uma avaliação ansiosa devido à semântica da linguagem ou às limitações atuais do compilador. No ano v2018.12, Rakudo, para que uma expressão da forma [ [ foo xx bar ] xx baz ]permaneça preguiçosa e não seja forçada a avaliar avidamente, ambas bar e bazdeve ser Inf. Por outro lado, my \table = [0 xx 100_000 for ^100_000]é preguiçoso, sem uso de Inf. (O último código está armazenando 100.000 Seqs na primeira dimensão em vez de 100.000 Arrays - say WHAT table[0]exibições em Seqvez de Array- mas a maioria dos códigos não será capaz de identificar a diferença - say table[99_999;99_999]ainda será exibida 0.)

5 Algumas pessoas pensam que é uma fraqueza aceitar que há mais de uma maneira de pensar e codificar soluções para determinados problemas. Na realidade, é uma força em pelo menos três aspectos. Primeiro, em geral, qualquer problema não trivial pode ser resolvido por muitos algoritmos distintos, com diferenças drásticas no perfil de desempenho. Esta resposta inclui uma abordagem já disponível com um Rakudo de um ano que será mais de mil vezes mais rápida que o Python na prática em alguns cenários. Segundo, uma linguagem deliberadamente flexível e multiparadigmática como o Raku permite que um codificador (ou equipe de codificadores) expresse uma solução que eles consideram elegante e sustentável, ou que apenas faz o trabalho com base no que elesacha melhor, não o que a linguagem impõe. Terceiro, o desempenho de Rakudo como um compilador de otimização atualmente é notavelmente variável. Felizmente, ele possui um ótimo perfil 6 , para que você possa ver onde está um gargalo e uma grande flexibilidade, para tentar uma codificação alternativa e isso pode produzir um código muito mais rápido.

6 Quando o desempenho importa, ou se você está investigando problemas de desempenho, consulte a página de documento da Raku sobre desempenho ; a página cobre uma variedade de opções, incluindo o uso do profiler Rakudo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.