Предисловие
Давеча слушал я Ruby NoName Podcast (кстати, весьма всем рекомендую), в котором услышал новость, что MacRuby 0.5 вышел из стадии бета-версии. Также господа ведущие сообщили, что в состав пакета MacRuby входит великолепный компилятор Ruby — MacRubyC, который теперь умеет компилировать в динамические библиотеки. Я, естественно, заинтересовался и решил поэкспериментировать.
Процесс
Итак, что делаем? Идём на официальный сайт MacRuby и скачиваем последнюю версию пакета. Распаковываем архив, ставим. Далее запускаем Терминал и пробуем:
Всё работает? Отлично.
Теперь попробуем на вкус компилятор MacRubyC. Для этого нам потребуется любой .rb файл, например, hello.rb, содержащий всего одну строку:
puts "Hello world!"
Пишем в терминале:
macrubyc hello.rb -o hello
На выходе получается аккуратный исполняемый файл, который можно спокойно запускать, как обычное Unix-приложение:
Однако, если мы попытаемся запустить нашу программу на другой машине, где не стоит MacRuby, то результат будет неутешителен — программе не хватает динамической библиотеки MacRuby:
Выход есть — статическая компиляция.
Но и тут-то не всё так просто: если у вас не стоит LLVM, то программа не откомпилируется:
Самый логичный выход — поставить этот самый LLVM:
svn co -r 82747 llvm.org/svn/llvm-project/llvm/trunk llvm-trunk
cd llvm-trunk
./configure
UNIVERSAL=1 UNIVERSAL_ARCH="i386 x86_64" ENABLE_OPTIMIZED=1 make -j2
sudo env UNIVERSAL=1 UNIVERSAL_ARCH="i386 x86_64" ENABLE_OPTIMIZED=1 make install
Заранее предупреждаю, LLVM довольно долго собирается, у меня это заняло около часа.
Самое вкусное
Наши программы могут спокойно компилироваться в статике и, соответственно, запускаться на чужой машине. Это замечательно. Что ж, давайте проведём маленькое исследование.
Во-первых, размер: исходный код hello.rb весит всего 19 байт. Если мы посмотрим на размер первого бинарника, то увидим, что он весит всего 13,6 килобайт. Не так уж и много, как может показаться. Но ведь мы его скомпилировали в динамике! Давайте взглянем на размер бинарного файла, откомпилированного в статике — 14,3 мегабайт! А это ведь только «Hello world». Впрочем, сильно волноваться не стоит: на самом деле с увеличением количества кода, размер исполняемого файла не так уж и сильно будет возрастать. Вполне очевидно, что в этих 14 мегабайтах упакованы библиотеки, которые программа неумышленно тащит за собой.
Теперь давайте коснёмся самого интересного — производительности.
Для того, чтобы замерить производительность, напишем программу, состоящую всего из одной строчки:
10000000.times { |i| puts i }
Затем откомпилируем нашу программу в статике и динамике, соответственно, с опцией --static и без неё:
macrubyc cycle.rb -o cycle
macrubyc cycle.rb --static -o cycle-static
Для того, чтобы посмотреть, как быстро (или медленно) выполняются наши программы, воспользуемся встроенной Unix-утилитой time.
Замеры проводились несколько раз, но результат в целом одинаков. Что же мы видим? Мы видим, что программа, исполнененная интерпретатором, выполняется практически на порядок быстрее чем стэндэлон программа. При этом, статическая компиляция выигрывает несколько секунд у динамической.
Но если взять тесты посерьёзней, например, функцию Мандельброта, то всё становится на свои места: скомпилированная программа выполняется на порядок быстрее — всего 0,32 секунды, тогда как интерпретированная программа выполняется около 8 секунд. Подобные же результаты наблюдаются, если в качестве тестовой функции взять функцию Такеючи (около 21 секунды при интерпретации и 0,5 секунды при компиляции):
Вместо послесловия
Вот, собственно, и всё. В целом выводы неоднозначны: с одной стороны компилированная программа отстаёт в линейных случаях, с другой стороны при компиляции используется хорошая оптимизация, которая даёт хороший результат в более сложных случаях. Тем не менее, решение об использовании компиляции в своих проектах принимать вам.