Сегодня я случайно наткнулся на эту ссылку, ища вопрос, который у меня был в Node. Сначала я не особо задумывался над этим, но читать дальше было немного интересно, так как, по всей видимости, это было сделано и на C, и на Go, и на JS, конечно же, это должно быть сделано и в Crystal :)

Если серьезно, то это также была возможность лучше узнать некоторые функции Crystal, а именно Slices, и некоторые тесты по быстрому конвейерному выводу.



Насколько быстро да в Node?
Когда разработчики устали говорить« да
на такие вещи, как пользовательские соглашения, установки или подтверждения в… hackernoon. com »



Вышеупомянутая статья привела меня к этой статье, где кто-то реализовал ее на Go, другом языке, который я немного изучал в последнее время.



Теперь я подумал, что это был хороший небольшой эксперимент, который можно было бы провести и в Crystal, и сравнить обе реализации с точки зрения ясности кода и скорости, которой я мог достичь. Я должен отметить, что я использую MacBook Pro 2013 года выпуска со старым процессором и старой памятью, поэтому скорость, которую я получаю, ниже, чем в других примерах, но все же сопоставима, поскольку они находятся на одном компьютере.

По сути, то, что мы делали, - это печать серии «y» с разрывом строки, например:

Вот и все, это продолжается вечно. Но мы используем инструмент под названием pv, чтобы проверить пропускную способность, которую мы можем достичь. Из документации pv:

pv позволяет пользователю видеть прогресс данных в конвейере, предоставляя такую ​​информацию, как истекшее время, процент выполнения (с индикатором выполнения), текущая пропускная способность, общий объем переданных данных и расчетное время прибытия. .

Да в Go.

Я взял тот же код, что и Мэт Эванс в Go, и запустил его на своем компьютере. Код выглядит так:

И результат:

2,38 ГиБ / с - вот результат, который я получил с кодом Go.

Да в кристалле.

Сначала я попробовал простейшее возможное решение:

И результат:

Хорошо, это было довольно медленно: 1,43 M МБ / с почти в 1500 раз медленнее, чем решение Go. Не так уж и странно, поскольку put выполняет для нас некоторое форматирование и добавляет новую строку, мы также печатаем их по одному, так что узким местом явно будет вызов put столько раз.

Но решение Go использовало буферизацию и передавало результаты в стандартный вывод. Можем ли мы сделать что-то подобное в Crystal?

Вторая попытка.

Оказывается, можем. Есть даже несколько способов сделать это, и я решил использовать фрагменты для буферизации, прежде чем мы будем передавать результат по конвейеру. Раньше я нечасто использовал фрагменты, и они не особо освещены в какой-либо литературе, которую я видел, поэтому я не тороплюсь, чтобы быстро объяснить, что это такое. Из документации Crystal:

Slice - это Pointer с соответствующим размером.

В то время как указатель небезопасен, поскольку при чтении и записи в него не выполняются проверки привязки, чтение и запись в срез включают проверки привязки. Таким образом, фрагмент - безопасная альтернатива Pointer.

Похоже, что мы могли бы здесь использовать! Дополнительным преимуществом является то, что их можно напрямую передать в метод, называемый:

STDOUT.write

Теперь код, который у меня получился, выглядел так:

И результат:

Намного лучше. 3,00 ГиБ / с, что даже значительно быстрее, чем реализация Go, у которой на моем MacBook было 2,38 ГиБ / с.

Уведомление о реализации:

В реализации Crystal мы выполняем проверки при создании объекта (if… else проверяет, является ли индекс четным числом или нет). Мы также преобразуем «y» и «\ n» в байты на каждой итерации (что означает 16384 раза). Для небольшого увеличения производительности мы могли бы сделать то же самое, что и в реализации Go, которая просто записывает байты. Я тестировал, и разница составила менее 5% прироста производительности (на некоторых запусках разницы не было). Вероятно, он будет увеличиваться по мере увеличения размера буфера, поэтому для 100% равного сравнения следует использовать этот метод. Но поскольку в этом примере разница была незначительной, я выбрал краткую, легко читаемую версию, приведенную выше.

Вывод.

Go часто приписывают чрезвычайно быстрой работе, но Crystal работает очень хорошо и быстрее, чем Go в этом небольшом примере. И хотя синтаксис сам по себе является субъективной темой, кто не скажет, что код Crystal прекрасен?

Если вы еще не пробовали Crystal, попробуйте серьезно.

Если вы хотите пояснить код, дайте мне знать в комментариях, и я добавлю его в статью.

До встречи!