Почему Raku так плохо работает с многомерными массивами?

10

Мне любопытно, почему Раку так плохо манипулирует многомерными массивами. Я сделал быстрый тест, инициализирующий 2-мерную матрицу в Python, C # и Raku, и прошедшее время на удивление велико для последующего.

Для раку

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

Для питона

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

Я делаю не так? Кажется, слишком большая разница.

Javi
источник
5
Это только медленно для фасонных многомерных массивов (например, тот, в котором вы определяете его @grid[4000;4000]), код Python не использует фасонный массив, и если вы попробуете то же самое в Raku, вы получите намного лучшее время назад: my @grid = [[0 xx 4000] xx 4000]; это означает, что вы должны получить доступ с помощью @grid[0][0]не @grid[0;0]. Я думаю, что это в основном потому, что фигурные массивы все еще находятся в стадии разработки.
Шимон Проктор
1
На мою машину @grid[1000;1000] = [[0 xx 1000]xx1000]ушло 12 секунд. @grid = [[0 xx 1000]xx1000]взял 0,6, так что ... да. Я бы избегал фигурных массивов.
Шимон Проктор
5
@Scimon, вы все еще можете использовать аксессор [;] для бесформенных массивов. my @grid = [[$++ xx 100] xx 100]; say @grid[0;1]; say @grid[1;1]возвращает 1 и 101 соответственно
user0721090601
Потрясающие! Это делает вещи проще.
Шимон Проктор
2
Фасонные многомерные массивы еще не получили такой оптимизации, как во многих других областях Ракудо.
Элизабет Маттийсен

Ответы:

13

Первоначальное прямое сравнение

Я начну с кода, который гораздо ближе к вашему коду Python, чем к вашему собственному переводу. Я думаю, что код Raku, который наиболее прямо эквивалентен вашему Python:

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

Этот код объявляет идентификатор без сигилов 1 . Это:

  • Капли "шейпинг" ( [4000;4000]в my @table[4000;4000]). Я уронил его, потому что ваш код на Python этого не делает. Формирование дает преимущества, но влияет на производительность. 2

  • Использует связывание вместо назначения . Я переключился на связывание, потому что ваш код Python выполняет связывание, а не присваивание. (Python не делает различий между ними.) Хотя подход Raku по присваиванию дает фундаментальные преимущества, которые стоит иметь для общего кода, он имеет последствия для производительности. 3


Этот код, с которого я начал свой ответ, все еще медленный.

Во-первых, код Raku, запускаемый с помощью компилятора Rakudo с декабря 2018 года, примерно в 5 раз медленнее, чем ваш код Python, с использованием интерпретатора Python с июня 2019 года на том же оборудовании. 3

Во-вторых, и код Raku, и код Python работают медленно, например, по сравнению с вашим кодом C #. Мы можем сделать лучше ...

Идиоматическая альтернатива, которая в тысячу раз быстрее

Следующий код стоит рассмотреть:

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

Несмотря на то, что этот код соответствует условному массиву из 10 миллиардов элементов, а не простому элементу из 16 миллионов элементов в вашем коде Python и C #, время выполнения его на настенном такте меньше, чем половина кода Python, и всего в 5 раз медленнее, чем C # код. Это говорит о том, что Rakudo выполняет код Raku в тысячу раз быстрее, чем эквивалентный код Python, и в сто раз быстрее, чем код C #.

Код Raku выглядит намного быстрее, потому что таблица лениво инициализируется с помощью xx Inf. 4 Единственная значительная работа выполняется на sayлинии. Это вызывает создание 100 000 массивов первого измерения, а затем заполнение только 100 000-го массива второго измерения 100 000 элементов, чтобы sayможно было отобразить 0удержание в последнем элементе этого массива.

Есть больше, чем один способ сделать это

Одна из проблем, лежащих в основе вашего вопроса, заключается в том, что всегда есть несколько способов сделать это. 5 Если вы столкнулись с низкой производительностью кода, для которого критична скорость, то, как я это делал, кодирование по-другому может привести к значительным изменениям. 6

(Другой действительно хороший вариант - задать ТАК вопрос ...)

Будущее

Рака тщательно разработана , чтобы быть очень optimiz состояния , т.е. в состоянии в один день работать гораздо быстрее данный достаточной работу компилятора в течение ближайших лет , чем, скажем, Perl 5 или Python-теоретически может, когда - нибудь работать, если они не проходят через наземный до редизайн и годы соответствующей работы компилятора.

Несколько неплохая аналогия - то, что произошло с производительностью Java за последние 25 лет. Rakudo / NQP / MoarVM находятся примерно на полпути к процессу созревания, который прошел стек Java.

Сноски

1 Я мог бы написать my $table := .... Но объявления формы my \foo ...исключают возможность рассмотрения сигил и позволяют использовать их =вместо :=идентификатора сигилла. (В качестве бонуса «вырезание сигилы» приводит к бесплатному идентификатору, знакомому кодировщикам на многих языках, которые не используют сигилы, которые, конечно, включают в себя Python и C #.)

2 Формирование может однажды привести к более быстрым операциям массива для некоторого кода. Между тем, как уже упоминалось в комментариях к вашему вопросу, в настоящее время он явно делает обратное, значительно замедляя его. Я предполагаю, что это в значительной степени потому, что каждый доступ к массиву в настоящее время наивно динамически проверяется на предмет границ, медленно все сокращается, и также не было никаких попыток использовать фиксированный размер, чтобы ускорить процесс. Кроме того, когда я попытался найти быстрое обходное решение для вашего кода, мне не удалось найти его с использованием массива с фиксированным размером из-за многих операций с массивами с фиксированным размером, которые в настоящее время не реализованы. Опять же, мы надеемся, что они будут реализованы однажды, но, по-видимому, они не стали достаточной проблемой для того, чтобы кто-либо работал над их реализацией на сегодняшний день.

3 На момент написания этой статьи TIO использует Python 3.7.4 с июня 2019 года и Rakudo v2018.12 с декабря 2018 года. Производительность Rakudo в настоящее время улучшается со временем значительно быстрее, чем у официального интерпретатора Python 3, поэтому я бы сказал, ожидайте, что разрыв между последним Rakudo и последним Python, когда Rakudo медленнее, будет значительно меньше, чем указано в этом ответе. В частности, текущая работа значительно улучшает выполнение заданий.

4 по xx умолчанию используется ленивая обработка, но некоторые выражения требуют тщательной оценки из-за языковой семантики или текущих ограничений компилятора. В годовалом v2018.12 Ракудо, чтобы выражение формы [ [ foo xx bar ] xx baz ]оставалось ленивым и не было вынуждено оценивать с нетерпением, оба так bar и bazдолжны быть Inf. Напротив, my \table = [0 xx 100_000 for ^100_000]ленивый без использования Inf. (Последний код на самом деле хранит 100 000 Seqс в первом измерении, а не 100 000 Arrayс - say WHAT table[0]отображает, Seqа не Array- но большая часть кода не сможет обнаружить разницу - say table[99_999;99_999]все равно будет отображаться 0.)

5 Некоторые люди думают, что это слабость - признать, что существует более одного способа думать и кодировать решения данных проблем. На самом деле это сила как минимум в трех отношениях. Во-первых, в общем, любая заданная нетривиальная проблема может быть решена многими различными алгоритмами с существенными различиями в профиле производительности. Этот ответ включает в себя подход, уже доступный для годовалого Ракудо, который на практике будет более чем в тысячу раз быстрее, чем Python, в некоторых сценариях. Во-вторых, преднамеренно гибкий и многопарадигмальный язык, такой как Raku, позволяет кодеру (или команде кодеров) выражать решение, которое они считают элегантным и обслуживаемым, или которое просто выполняет свою работу, основываясь на том, что онидумать лучше, а не то, что навязывает язык. В-третьих, производительность Rakudo в качестве оптимизирующего компилятора в настоящее время заметно варьируется. К счастью, он обладает отличным профилировщиком 6 , поэтому можно видеть узкое место и большую гибкость, поэтому можно попробовать альтернативное кодирование, и это может привести к значительно более быстрому коду.

6 Если производительность важна или вы исследуете проблемы с производительностью, обратитесь к странице документации Raku по производительности ; На этой странице представлен ряд параметров, включая использование профилировщика Rakudo.

raiph
источник