Cайт веб-разработчика, программиста Ruby on Rails ESV Corp. Екатеринбург, Москва, Санкт-Петербург, Новосибирск, Первоуральск

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