Эта статья — демонстрация сравнительного анализа языков Elixir и Ruby: в ней пойдёт речь о том, какую производительность обеспечивают веб-фреймворки Phoenix и Rails при выполнении одних и тех же задач. Прежде чем перейти к примерам кода и результатам бенчмарков, ответим на несколько распространённых вопросов о тестах такого рода.
Если коротко, то Phoenix справился с заданием в 10,63 раза быстрее, чем Rails, обеспечив более низкую загрузку CPU.
ЧАВО
Не думаете ли вы, что пытаетесь сравнить несравнимое?
Вовсе нет. Приведённые тесты — прямое сопоставление полюбившихся особенностей Ruby (Rails) с Elixir (Phoenix). Elixir гарантирует предоставить то, что все так ценят в Ruby: скорость разработки, метапрограммирование, изящные API и DSL, но при этом работать быстрее, обеспечивая надёжную конкурентную модель и распределённую обработку данных. Цель данной статьи — выяснить, как уже полюбившиеся особенности Ruby без потери удобства программных интерфейсов реализуются в Elixir с точки зрения производительности веб-фреймворков.
Так ли необходимы бенчмарки?
Бенчмарки — не что иное, как средство заблаговременного обеспечения надёжности работы тестируемых программ. Но даже в этом случае бенчмарки всего лишь позволяют получить представление о каких-либо характеристиках. Поэтому мораль здесь такова: не стоит полагаться на бенчмарки, нужно всегда проводить проверки вручную.
Что конкретно вы сравниваете?
Elixir Phoenix Framework
Phoenix 0.3.1
Cowboy webserver (single Elixir node)
Erlang 17.1
Ruby on Rails
Rails 4.0.4
Rails 4.0.4
Puma webserver (4 воркера — по одному на каждое ядро)
MRI Ruby 2.1.0
Мы проводим сравнение быстродействия двух эквивалентных приложений на Phoenix и на Rails, причём для большей наглядности определённые задачи рассматриваются отдельно. Для оценки выполним следующие пункты.
Сопоставим запрос к серверу и направим его к соответствующему действию контроллера, разбирая именованные параметры
Внутри контроллера рендерим представление (в зависимости от заголовка Accept в запросе), которое помещается в родительский макет. Представления рендерятся посредством встроенных в язык шаблонизаторов (ERB, EEx).
В представлении рендерим коллекцию данных, полученных из контроллера.
Отправляем ответ клиенту.
Вот и всё. Мы тестируем типовое сопоставление маршрутов и отображение стека представлений, что явно выходит за рамки примера «Hello World». В обоих приложениях производится рендеринг макета, представлений и их составляющих для проведения анализа реальной производительности при выполнении основной задачи веб-фреймворка. Чтобы не перегружать IO в обоих приложениях отключены кэширование представлений и логирование запросов. Для проведения бенчмаркинга мы использовали инструмент wrk. Помимо тестирования на локальном сервере, мы также провели тестирование и на удалённом (основанном на heroku dynos) с целью исключения влияния этого инструмента на результаты, полученные в ходе локального эксперимента. Хватит болтовни, посмотрим, как выглядит код.
Phoenix показал в 10,63 раза большую производительность и более устойчивое среднеквадратическое отклонение времени задержки. Такие результаты доказывают реальные преимущества конкурентной модели Elixir. Одна нода Elixir использует все необходимые ресурсы CPU и требуемый объём памяти, в то время как веб-сервер Puma (Rails) для реализации многопоточности создаёт по одному процессу на каждом ядре CPU.
Phoenix:
req/s: 12,120.00
Stdev: 3.35ms
Max latency: 43.30ms
Rails:
req/s: 1,140.53
Stdev: 18.96ms
Max latency: 159.43ms
Phoenix
$ mix do deps.get, compile
$ MIX_ENV=prod mix compile.protocols
$ MIX_ENV=prod elixir -pa _build/prod/consolidated -S mix phoenix.start
Running Elixir.Benchmarker.Router with Cowboy on port 4000
$ wrk -t4-c100-d30S--timeout 2000 "http://127.0.0.1:4000/showdown"
Running 10s test @ http://127.0.0.1:4000/showdown
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 8.31ms 3.53ms 43.30ms 79.38%
Req/Sec 3.11k 376.89 4.73k 79.83%
121202 requests in 10.00s, 254.29MB read
Requests/sec: 12120.94
Transfer/sec: 25.43MB
Rails
$ bundle
$ RACK_ENV=production bundle exec puma -w 4
[13057] Puma starting in cluster mode...
[13057] * Version 2.8.2 (ruby 2.1.0-p0), codename: Sir Edmund Percival Hillary
[13057] * Min threads: 0, max threads: 16
[13057] * Environment: production
[13057] * Process workers: 4
[13057] * Phased restart available
[13185] * Listening on tcp://0.0.0.0:9292
$ wrk -t4-c100-d30S--timeout 2000 "http://127.0.0.1:9292/showdown"
Running 10s test @ http://127.0.0.1:9292/showdown
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 21.67ms 18.96ms 159.43ms 85.53%
Req/Sec 449.74 413.36 1.10k 63.82%
11414 requests in 10.01s, 25.50MB read
Requests/sec: 1140.53
Transfer/sec: 2.55MB
Результаты для Heroku (1 dyno)
Phoenix оказался в 8,94 раза производительнее, снова показав намного более устойчивое среднеквадратическое отклонение времени задержки и в 3,74 раза меньшую загрузку CPU. Пытаясь получить на сервере Phoenix такую же нагрузку на CPU, как и на сервере Rails, мы столкнулись с нехваткой доступных сокетов. Возможно, Phoenix-приложение показало бы большую производительность, если бы сети клиентов имели более высокую пропускную способность. В случае использования удалённого сервера значение среднеквадратического отклонения имеет особую важность. Rails-приложение не смогло обеспечить устойчивое время отклика, показав результат времени задержки более 8 секунд. На практике, Phoenix-приложение в нагруженном состоянии должно работать намного более стабильно, чем приложение на Rails.
Phoenix:
req/s: 2,691.03
Stdev: 139.92ms
Max latency: 1.39s
Rails:
req/s: 301.36
Stdev: 2.06s
Max latency: 8.36s
Elixir сочетает в себе удобство и скорость разработки Ruby с конкурентной моделью и отказоустойчивостью Erlang. Программируя на Elixir, можно одновременно пользоваться преимуществами двух этих языков, в связи с чем я призываю вас ознакомиться с Phoenix. Придётся ещё попотеть над доработкой Phoenix, чтобы он мог сравняться с мощной экосистемой Rails, но это только начало. На этот год у нас уже большие планы.
Если вы хотели бы собственноручно провести такое же тестирование, то созданные приложения находятся в свободном доступе на сайте Github. Интересно было бы посмотреть, какие результаты получились на разном железе, особенно на том, которое могло бы сильнее нагрузить Phoenix-приложение.
За предоставленную информацию об установке приложений Heroku и тестировании удалённых серверов благодарим Джейсона Стибса!
Один-два раза в неделю присылаем тёплые письма об Эликсире: переводы самых интересных статей до их появления в открытом доступе, анонсы событий и вкусные бонусы.
Обязательно подтверди почту, перейдя по ссылке в письме, иначе мы не сможем делиться с тобой полезностями.