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