Ruby YJIT vs C: Ускоряем Ruby, переписав C… на Ruby
...
По большей мере подобные бенчмарки бессмысленны. Python был самым медленным языком в бенчмарке, однако в то же время самым используемым языком Github на октябрь 2024 года. На Ruby написано одно из самых крупных веб-приложений в мире. Недавно я запускал бенчмарк производительности веб-сокетов между веб-сервером Ruby Falcon и node.js, и результаты Ruby были близки к результатам node.js. Вы чаще выполняете миллиард итераций цикла или используете веб-сокеты?
Язык программирования должен быть достаточно эффективным, за этим идёт полезность языка и тип ваших задач, и продуктивность языка перевешивает скорость выполнения миллиарда итераций цикла или намеренно выбранной неэффективной реализации метода Фибоначчи.
Тем не менее:
- Мир программирования любит бенчмарки
- Быстрый бенчмарк не имеет ценности на практике, но привлекает интерес людей к языку. Кто-то может сказать, что он упрощает масштабирование производительности, но об этом можно поспорить
- То, что выбранный вами язык показывает себя не с лучшей стороны, раздражает. Здорово, когда можно сказать: «Я пользуюсь и наслаждаюсь этим языком, и он быстро работает во всех бенчмарках!»
Использование JIT в Ruby заметно повышает производительность, что, видимо, сильно порой раздражает приверженцев других языков программирования, которые продолжают "огалтело орать на каждом углу", что "Ruby мёртв". Не дождётесь, уважаемые! ;-))) В комментариях к статье не так и много высказываний по делу, а всё свелось снова к обсуждению динамической типизации, как якобы огромного недостатка Ruby. Успокойтесь и выдохните! Ваша статическая типизация ненамного поможет вам в написании правильно работающего кода (вспомним басню И.А. Крылова «Квартет»), статическая типизация не особо поможет говнокодерам. В Ruby предложено элегантное решение указания типов, которое не засоряет код объявлениями и указаниями типов, а нужно лишь действительно для проверки корректности программы - механизм этот RBS, Understanding RBS, Ruby's new Type Annotation System
Очень важно ещё и понимать, что не только сам язык должен быть быстрым в исполнении, но и написание программ на нём должно быть быстрым и эффективным. Какой толк от вашей сверхбыстрой системы, если на её написание вы потратите месяцы и годы, а та же задача может быть реализована на Ruby за недели? В то время, как вы пыхтите над оптимизацией кода, разумные люди предпочтут пользоваться готовой работающей (может чуть медленнее) программой, чем ожидать появления наконец-то вашего сверхбыстрого чуда-чудесного. Ruby - язык продуктивного программирования - этим очень многое сказано. Очень убедительно рассказано тут про это.
Ошибки несоответствия типов, если таковые вдруг возникли, довольно легко и быстро обнаруживаются, программист Ruby найдёт корректный и элегантный способ "подружить" разные типы между собой. Про "утиную типизацию" и "monkey patch", которая якобы может испортить код программ, не ожидающих некоторого поведения от определённых классов тоже можно давно забыть, так как в Ruby существует, например, механизм Refinements.
Не забываем, кстати, и о параллелизме, который является частью самого языка Ruby - в наше время все современные процессоры имеют множество ядер процессора (отдельных процессоров) - при умелом использовании параллельное исполнение разных частей программ может значительно повысить производительность. И тут, о Боже, сразу в мусорку улетает Python (с его GIL) - язык, популярность которого в последнее время искусственно завышена за счёт учителей-недопрограммистов и новичков, которые шерстят интернет в поисках учебных материалов по Python для своих лекций и новичков, ищущих готовые решения.
С внедрением механизма JIT в Ruby вскоре многие другие языки программирования потеряют свои мнимые преимущества в виде более быстрого исполнения кода.
Тест производительности Ruby с YJIT
Дабы не быть голословным, я провёл свой эксперимент. Использовал Ruby 3.4.5 с YJIT.
require "benchmark" def fib(n) return n if n <= 1 fib(n - 1) + fib(n - 2) end Benchmark.bm do | make | i = 0 5.times do make.report { puts fib(30 + i) } i += 1 end end
результаты:
# ruby 832040 1346269 2178309 3524578 5702887 user system total real 0.051248 0.000000 0.051248 ( 0.051280) 0.079303 0.000000 0.079303 ( 0.079312) 0.124613 0.000000 0.124613 ( 0.124618) 0.207433 0.000000 0.207433 ( 0.207441) 0.330358 0.000000 0.330358 ( 0.330383) # ruby --yjit 832040 1346269 2178309 3524578 5702887 user system total real 0.005949 0.000786 0.006735 ( 0.006736) 0.010470 0.000000 0.010470 ( 0.010471) 0.017448 0.000008 0.017456 ( 0.017502) 0.028922 0.000000 0.028922 ( 0.028929) 0.049098 0.000000 0.049098 ( 0.049102)
И для сравнения ещё PHP и Python
# PHP 8.2.28 <?php function fib($n) { if ($n <= 1) return $n; return fib($n - 1) + fib($n - 2); } for($i = 0; $i < 5; $i++) { $t = microtime(true); print fib(30 + $i) . "\n"; print(microtime(true) - $t) . "\n"; } ?> 832040 0.023648023605347 1346269 0.038004875183105 2178309 0.062031984329224 3524578 0.1047568321228 5702887 0.16733598709106
# Python 3.11.2 import time def fib(n): if (n <= 1): return n return fib(n - 1) + fib(n - 2) i = 0 while (i < 5): start = time.time() print(fib(30 + i)) elapsed = (time.time() - start) print(elapsed) i += 1 832040 0.06262660026550293 1346269 0.1026451587677002 2178309 0.16524362564086914 3524578 0.26902174949645996 5702887 0.4344816207885742