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