Inventor. Rebel. Entrepreneur.
0,0
рейтинг
2 июля 2014 в 19:03

Разработка → Немного тестов производительности сетевых фреймворков из песочницы

Привет Хабр! Пару месяцев назад я захотел провести тестирование производительности некоторых сетевых фреймворков, c целью понять насколько большая разбежка между ними. Надо ли использовать Node.js там, где хотелось бы Python с Gevent или нужен Ruby с его EventMachine.

image

Я хочу обратить ваше внимание на то, что эти материалы не являются руководством к выбору фреймворка и могут содержать спорные моменты. Я вообще не собирался публиковать результаты этого исследования, но когда они попадались мне на глаза я ловил себя на мысли, что это может быть кому-нибудь полезно. Теперь я начну забрасывать вас графиками.

1. Text / Httperf / VPS 1 CPU, 512Mb RAM


Первый тест я провёл на самом дешёвом VPS DigitalOcean (1 Core, 512Mb RAM, 20Gb SSD). Для тестирование производительности использовалась утилита httperf. Что бы произвести необходимую нагрузку были задействованы VPS такой же конфигурации, в количестве 5 штук. Для одновременного запуска теста на всех клиентах я использовал утилиту autobench со следующими параметрами:

autobench_admin --single_host --host1 example.com --port1 8080 --uri1 / --low_rate 50 --high_rate 600 --rate_step 10 --num_call 10 --num_conn 6000 --timeout 5 --clients XX.XX.XX.XX:4600,XX.XX.XX.XX:4600,XX.XX.XX.XX:4600,XX.XX.XX.XX:4600,XX.XX.XX.XX:4600 --file bench.tsv

Это тест начинает выполнение с 50 соединений в секунду (10 запросов через одно соединение) и с шагом в 10 соединений в секунду достигает 600. Каждый тест устанавливает всего 6000 соединений и все запросы, которые не были обработаны в течение 5 секунд, считаются ошибкой.

Все HTTP серверы делают одно и то же, а именно возвращают строку «I am a stupid HTTP server!» на каждый запрос. Результаты получились следующими (по оси Х – количество запросов в секунду):

Нагрузка на процессор



Потребление оперативной памяти (в % от 512Mb)



Количество ответов



Время ответа (в миллисекундах)



Количество ошибок



Как только мы достигаем 100% использования CPU, потребление оперативной памяти начинает расти, количество ответов падает, время ответа на каждый запрос растёт и начинают появляться ошибки. Как я писал выше, каждый запрос, который не получил ответ в течение 5 секунд, считается ошибкой и здесь именно это и происходит, это можно проследить на графике «Время ответа».

Результаты (в скобках количество обработанных запросов без ошибок):

  1. Gevent (4700)
  2. Express.js (3600)
  3. Eventlet (3200)
  4. Tornado (2200)

Я никогда не бываю доволен своей работой полностью, поэтому уже через пару часов я решил, что тестировать производительность на VPS не самый лучший выбор. Между фреймворками разница в производительности понятна и какие-то выводы сделать можно, но узнать сколько клиентов мы в состоянии обслужить на одном ядре настоящего процессора мы не можем. Одно дело делить с кем-то неизвестные ресурсы и совсем другое когда все ресурсы известны и в нашем распоряжении.

2. Text / Httperf / Intel Core i7-4770 Quad-Core Haswell, 32 GB DDR3 RAM


Для следующего теста я арендовал выделенный сервер у Hetzner (EX40) с процессором «Intel Core i7-4770 Quad-Core Haswell» и 32 GB DDR3 RAM.

На этот раз я создал 10 VPS, которые будут создавать необходимую нагрузку и запустил autobench со следующими параметрами:

autobench_admin --single_host --host1 example.com --port1 8080 --uri1 / --low_rate 50 --high_rate 1500 --rate_step 50 --num_call 10 --num_conn 15000 --timeout 5 --clients XX.XX.XX.XX:4600,XX.XX.XX.XX:4600,XX.XX.XX.XX:4600,XX.XX.XX.XX:4600,XX.XX.XX.XX:4600 ... --file bench.tsv

Это тест начинает выполнение с 50 соединений в секунду (10 запросов через одно соединение) и с шагом в 50 соединений в секунду достигает 1500. Каждый тест устанавливает всего 15000 соединений и все запросы, которые не были обработаны в течение 5 секунд, считаются ошибкой.

Исходный код серверов тот же, что и в первом тесте. Запущена одна копия сервера, которая использует только 1 ядро. В этот тест я добавил фреймворки Twisted 13.2 и Eventmachine 1.0.3. Потребление памяти я удалил из результатов теста потому что разница, по современным меркам, ничтожна. Не буду тянуть кота за хвост, вот результаты:

Нагрузка на процессор




Количество ответов




Время ответа (в миллисекундах)




Количество ошибок




Тут, как и прежде, упёрлись в CPU, чего и следовало ожидать. В среднем, производительность здесь выше в 3 раза, чем на VPS DigitalOcean (1 Core, 512Mb), из чего можно сделать соответствующие выводы о количестве выделенных нам ресурсов.

Результаты (в скобках количество обработанных запросов без ошибок):

  1. Eventmachine (подробности ниже)
  2. Gevent (12500)
  3. Express.js (11500)
  4. Eventlet (9000)
  5. Twisted (7000)
  6. Tornado (6500)

Eventmachine


Eventmachine меня удивил своей производительностью и ушёл далеко от конкурентов, из-за чего мне пришлось увеличить нагрузку до 25000 запросов в секунду специально для него. Результат на графиках:

Нагрузка на процессор




Количество ответов




Время ответа (в миллисекундах)




Количество ошибок




У меня есть подозрения, что и 30 000 запросов он бы смог обработать, но мне надо было двигаться дальше, поэтому я не смог в этом убедиться. Вообще я к этому моменту уже знал, что буду использовать Python для своего проекта, так что фреймворки на других языках мне нужны были просто для сравнения.

3. Files / Siege / Intel Core i7-4770 Quad-Core Haswell, 32 GB DDR3 RAM


Как я писал выше, я не бываю доволен своей работой полностью, поэтому я лёг спать с чувством выполненного долга, а проснулся с мыслью «нужно больше тестов!». Отдавать строку текста на каждый запрос это конечно хорошо, но это не единственная функция веб-сервера, значит будем раздавать файлы.

Для этого теста я использовал 10 VPS, что бы создать необходимую нагрузку. Экспериментальным путём я выяснил, что на 1 VPS DigitalOcean, в среднем, выделен канал 100Mbps. Сервер у меня был с каналом 1Gbps и мне надо было его полностью нагрузить. Файлами для раздачи послужили изображения с интернет-магазина в количестве 10 000 штук, разных размеров. Для создания нагрузки я использовал утилиту siege со следующими параметрами:

siege -i -f fileslist.txt -c 55 -b -t1M

В filelist.txt хранится список файлов, устанавливается 55 соединений и через них мы начинаем долбить сервер запросами в течение 1-й минуты. Файлы при этом выбираются случайным образом из списка fileslist.txt. Опредёленно стоит учесть, что тест этот запускается на 10 машинах одновременно, а значит мы устанавливаем не 55, а 550 одновременных соединений. Более того, эту опцию я постоянно менял от 5 до 55 с шагом в 5, увеличивая тем самым нагрузку на сервер, и устанавливая от 50 до 550 одновременных соединений.

Вот что получаем (по оси Х – количество одновременных соединений):

Количество выполненных запросов



Количество обработанных запросов в секунду



Нагрузка на процессор (в %)



Потребление оперативной памяти (в % от 32Gb)



Нагрузка на канал связи (мегабайт в секунду)



Среднее время ответа на запрос (в секундах)



В этом тесте я добавил потребление оперативной памяти, а также веб-сервер nginx для сравнения. Здесь узким местом является канал связи, причём 1-го ядра достаточно для того, что бы весь этот канал в 1Gbps загрузить.

Результаты (в скобках количество обработанных запросов без ошибок):

  1. Nignx (100175)
  2. Eventlet (97925)
  3. Gevent (96918)
  4. Express.js (96162)
  5. Twisted (85733)
  6. Tornado (83241)

4. GridFS / Siege / Intel Core i7-4770 Quad-Core Haswell, 32 GB DDR3 RAM


На этом можно было завершать статью, но я хотел использовать MongoDB GridFS в своём проекте, поэтому решил посмотреть, как изменится производительность с её использованием. Данный тест аналогичен 3-му, за исключением того, что все изображения в количестве 10 000 штук я залил в MongoDB и переписал веб-серверы так, что бы они раздавали файлы из базы. Итак, что мы получаем:

Количество выполненных запросов



Количество обработанных запросов в секунду



Нагрузка на процессор (в %)



Потребление оперативной памяти (в % от 32Gb)



Нагрузка на канал связи (мегабайт в секунду)



Среднее время ответа на запрос (в секундах)



Количество ошибок



Во время теста у Gevent были ответы с ошибками, поэтому я добавил график «Количество ошибок». В целом GridFS вполне можно использовать, но стоит учитывать, что сама база создаёт немалую нагрузку на CPU, а у меня было 7 свободных ядер в её распоряжении, когда с файловой системой все гораздо проще.

Результаты (в скобках количество обработанных запросов без ошибок):

  1. Express.js (88714)
  2. Gevent (86182)

Выводы


  • MacBook Pro Retina действительно отрабатывает 9 часов на одном заряде.
  • Node.js не единственный инструмент, как считают некоторые, для разработки сетевых приложений.
  • Gevent выдаёт очень хорошую производительность.
  • Оформление статьи занимает больше времени, чем её написание.
  • Тестирование производительности сложный процесс, который занимает много времени.

Если серьёзно, все зависит от условий, при которых будет работать ваш проект. Можно провести огромное число тестов, но когда сервис будет написан, все скорее всего будет совсем по другому. Например, при увеличении числа картинок с 10 000 до 1 000 000 узким местом уже становится производительность жёсткого диска, а не канал связи.

Материалы

Если вы решите провести собственное тестирование или более подробно изучить моё, то этот список вам должен помочь.

Отчёты

Полные отчёты с индивидуальными графиками и цифрами можно скачать по этим ссылкам:

  1. Text / Httperf / VPS 1 CPU, 512Mb RAM
  2. Text / Httperf / Intel Core i7-4770 Quad-Core Haswell, 32 GB DDR3 RAM
  3. Files / Siege / Intel Core i7-4770 Quad-Core Haswell, 32 GB DDR3 RAM
  4. GridFS / Siege / Intel Core i7-4770 Quad-Core Haswell, 32 GB DDR3 RAM

Иструменты

В своих тестах я использовал:


Фреймворки

В тестах принимали участие:


Всем спасибо за внимание.

Подписывайтесь на меня в Twitter, я рассказываю о работе в стартапе, своих ошибках и правильных решениях, о python и всём, что касается веб-разработки.

P.S. Я ищу разработчиков в компанию, подробности у меня в профиле.
Vladimir Kozlovski @vladkozlovski
карма
52,0
рейтинг 0,0
Inventor. Rebel. Entrepreneur.
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (59)

  • +1
    Тогда уж стоило и с EventMachine для руби сравнить)
    • 0
      Я сравнил только для текста с EventMachine. Когда я дошел до теста с раздачей файлов, я уже для себя решил, что буду использовать Python. Поэтому такой тест не проводил, к сожалению.
      • 0
        Почему решили, что python, если em производительнее?
        • +1
          Python мне ближе идеологически. Честно говоря, я сейчас сожалею, что не провел тогда более подробное тестирование с EventMachine, с раздачей файлов. И мне было бы интересно сравнить его с asyncio в Python 3.4, что я как-нибудь обязательно сделаю.
          • +2
            Раздачей должен заниматься nginx. Он ведь у вас всё равно стоит на фронте, судя по порту 8080 в коде. Не думаю, что результаты заполнения заголовка x-sendfile как-то будет отличасться от отправки просто текста.
            • 0
              Задачей было проверить скорость отдачи файлов с использованием различных фреймворков. Более того, в моем проекте было непонятно где именно будут хранится файлы, в файловой системе или GridFS или еще где-то. Понятно, что в продакшене будут использоваться балансировщик, кеширование, авторизация и прочие моменты и результаты тестов будут другие.
              • 0
                Небольшой офтоп, но всё-таки, как вам производительность/простота/удобство GridFS? Я для своих just-for-fun проектов всегда страдаю перфекционизмом с минимальными затратами, например, поднимаю ноды на своих домашних серверах (2x Intel Atom, Intel Quad Core, Raspberry Pi) и при этом настраиваю между ними IPSec, конфигурирую балансировку и прочие «ненужные чудеса». Так вот я смотрел в сторону где бы хранить файлы (миллионы их объёма до 50МБ). Набрёл на WeedFS в итоге и вот пока для моего гордого кластера это самое удобное решение, при этом единственный метод коммуникации с ним — HTTP API, то есть с Nginx связывается на ура без дополнительных прослоек.
                • 0
                  Я отказался от использования GridFS в своем проекте, хотя MongoDB мне нравится. До тех пор, пока размер данных небольшой, я все храню в обычной файловой системе и это, на мой взгляд, самое лучшее решение. Когда встанет вопрос хранения, то я думаю в сторону Ceph FS, но об этом решении надо говорить с тем, кто имел опыт его использования.

                  Про GridFS можно говорить много, но есть несколько моментов, которые стоит учитывать. Это обычная MongoDB, а GridFS это просто удобный интерфейс для хранения файлов в ней. Запускать MongoDB лучше всего в изолированном окружении, в количестве 2-х экземпляров + арбитр, что привносит доп. расходы. Писать мы можем в нее только последовательно и запись производиться через 1 сервер (если нет шардинга). Если у вас преимущественно чтение, в редких случаях запись, не требуется редактировать или удалить старые файлы, есть возможность выделить ресурсы на запуск нескольких экземпляров MongoDB, то можете рассмотреть этот вариант. В остальных же случаях надо искать другое решиние.
                  • 0
                    Решил дополнить немного. Большинство ограничений можно обойти, но на практике мы получим низкую производительность. Удалить старые файлы из GridFS мы конечно можем, но больше свободного места мы таким образом не получим. Редактирование файлов можно выполнить вручную (когда-то это можно было делать штатными средствами), но тогда чтение будет не последовательное и мы получим низкую производительность (насколько я помню, именно по этой причине они выпилили эту возможность).
  • 0
    что-то как-то rps на i7 не очень, неделю назад бенчмаркал undertow на i7 4790, выдавало примерно 440к rps на 400 подключений
    • +1
      Я использовал только 1 ядро процессора в своем тесте, это стоит учитывать.
      • 0
        а какой смысл так делать?
        • 0
          Чистота эксперимента:
          Все эти «фреймворки» асинхронные, поэтому нет большой разницы в сколько процессов их гонять.
          С другой стороны многоядерная машинка начнет I/O выполнять на других ядрах — это смажет результат.
        • +5
          Цель была выяснить не производительность процессора, а производительность разных фреймворков. Зная производительность на 1 ядре я могу примерно понять производительность на 2-ух и более.
          • 0
            Это вроде правильно, но вот есть ли полная уверенность, что внутри ничего не распараллеливается и ничего не оптимизировано под многопоточность? :)
            • 0
              У меня есть понимание, как все эти фреймворки работают. Ну и конечно во время теста была бы видна нагрузка на другие ядра. Так что мой ответ – да, уверенность есть.
              • 0
                Ах, вот оно что, прошу прощения, видимо я пропустил описание, что виртуалкам было доступно более одного ядра, благодарю за разъяснение.
  • +3
    Легенду почти не видно…
  • +1
    2014 года на дворе, а вы почему-то очень старый Ruby 1.8.7 гоняете, при этом он с Eventmachine показывает блестящие результаты, но вы все равно выбираете иное. Я видимо чего-то не понимаю.
    • 0
      Спасибо за замечание. EventMachine я в тесты добавил просто для сравнения, как и node.js. Я с самого начала не планировал их использовать в своем проекте. Производительность у него действительно отличная. У меня было желание проверить его с раздачей файлов, но время на исследование было ограничено.
  • 0
    Ещё было бы интересно сравнить с потоковым фреймворком, bottle, flask или т.п. (зажатым в одно ядро), у них нет оверхеда на «евент-луп» и обработку асинхронного кода, но есть большее потребление памяти (в теории).
    • 0
      Оверхед на использование ивент-лупа перестаёт быть значительным уже при двух активных сокетах. Оверхед проявляет сильно только при одном сокете. Во всех остальных случаях ивентлуп на одном ядре должен быть быстрее N потоков на одном ядре.
      • 0
        Во всех остальных случаях ивентлуп на одном ядре должен быть быстрее N потоков на одном ядре.

        Это зависит от работы которую выполняет сервер, асинхронность не всегда «быстрее».

        Например в тестах выше Gevent показывает результат лучше чем например Twisted, а ведь у gevent нет классического ивент-лупа (там происходит переключение стеков).
        По простому, производительность асинхронности проявляется на «долгих» открытых коннектах.
        • 0
          Можете привести пример при N > 1? Можно практический. Можно в личку:)
          • 0
            Вот сейчас прогнал у себя на python3.4, в lxc на одно ядро (так же проверял с taskset):
            ab -n 20000 -c 100 10.0.3.16:8888/

            uwsgi --http :8888 --wsgi-file source.py --logto /dev/null --processes 1 --threads 10
            5946 rps
            6mb ram
            cpu ~40%
            Исходник от сюда: uwsgi-docs.readthedocs.org/en/latest/WSGIquickstart.html#the-first-wsgi-application

            tornadoweb
            2507 rps
            11mb ram
            cpu 100%
            Исходник от сюда: www.tornadoweb.org/en/stable/

            Кол-во передаваемых данных выравнено. Большее кол-во потоков для uwsgi профита не дало.
            По результатам видно что торнадо уперся в cpu. Так же тут ещё повлияло что в uwsgi варианте нет диспетчера урлов, да и всего прочего питон кода который отъедает процессор.

            Логи
            $ ab -n 20000 -c 100 http://10.0.3.16:8888/
            This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
            Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
            Licensed to The Apache Software Foundation, http://www.apache.org/
            
            Benchmarking 10.0.3.16 (be patient)
            Completed 2000 requests
            Completed 4000 requests
            Completed 6000 requests
            Completed 8000 requests
            Completed 10000 requests
            Completed 12000 requests
            Completed 14000 requests
            Completed 16000 requests
            Completed 18000 requests
            Completed 20000 requests
            Finished 20000 requests
            
            
            Server Software:
            Server Hostname:        10.0.3.16
            Server Port:            8888
            
            Document Path:          /
            Document Length:        11 bytes
            
            Concurrency Level:      100
            Time taken for tests:   3.363 seconds
            Complete requests:      20000
            Failed requests:        0
            Total transferred:      1100000 bytes
            HTML transferred:       220000 bytes
            Requests per second:    5946.81 [#/sec] (mean)
            Time per request:       16.816 [ms] (mean)
            Time per request:       0.168 [ms] (mean, across all concurrent requests)
            Transfer rate:          319.41 [Kbytes/sec] received
            
            Connection Times (ms)
                          min  mean[+/-sd] median   max
            Connect:        0    0   0.2      0       3
            Processing:     8   17   0.8     17      19
            Waiting:        7   16   0.8     16      19
            Total:          8   17   0.6     17      19
            
            Percentage of the requests served within a certain time (ms)
              50%     17
              66%     17
              75%     17
              80%     17
              90%     17
              95%     18
              98%     18
              99%     19
             100%     19 (longest request)
            
            
            
            $ ab -n 20000 -c 100 http://10.0.3.16:8888/
            This is ApacheBench, Version 2.3 <$Revision: 1528965 $>
            Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
            Licensed to The Apache Software Foundation, http://www.apache.org/
            
            Benchmarking 10.0.3.16 (be patient)
            Completed 2000 requests
            Completed 4000 requests
            Completed 6000 requests
            Completed 8000 requests
            Completed 10000 requests
            Completed 12000 requests
            Completed 14000 requests
            Completed 16000 requests
            Completed 18000 requests
            Completed 20000 requests
            Finished 20000 requests
            
            
            Server Software:        TornadoServer/3.2.2
            Server Hostname:        10.0.3.16
            Server Port:            8888
            
            Document Path:          /
            Document Length:        12 bytes
            
            Concurrency Level:      100
            Time taken for tests:   7.976 seconds
            Complete requests:      20000
            Failed requests:        0
            Total transferred:      4140000 bytes
            HTML transferred:       240000 bytes
            Requests per second:    2507.39 [#/sec] (mean)
            Time per request:       39.882 [ms] (mean)
            Time per request:       0.399 [ms] (mean, across all concurrent requests)
            Transfer rate:          506.86 [Kbytes/sec] received
            
            Connection Times (ms)
                          min  mean[+/-sd] median   max
            Connect:        0    0   0.4      0       8
            Processing:     3   40   2.6     39      52
            Waiting:        3   40   2.6     39      52
            Total:          8   40   2.5     39      52
            
            Percentage of the requests served within a certain time (ms)
              50%     39
              66%     39
              75%     40
              80%     40
              90%     42
              95%     46
              98%     48
              99%     50
             100%     52 (longest request)
            

            • 0
              Ну так а вы не думали что в случае с tornado как весь этот лишний код (диспетчер, парсер) как раз и съедает весь проц?
              Чтобы сранивать производительность blocking / non-blocking нужно сделать так, чтобы именно сеть была основным workload'ом.
              Ну для такого теста нужно тестировать в рамках одного фреймворка или вообще без него, используя лишь голые сокеты.
              • 0
                Ну так а вы не думали что в случае с tornado как весь этот лишний код (диспетчер, парсер) как раз и съедает весь проц?
                Я про этот «оверхед» выше и написал в отличие от многопоточных фреймворков:
                … у них нет оверхеда на «евент-луп» и обработку асинхронного кода,

                И это опровергает ваш комментарий выше
                Оверхед на использование ивент-лупа перестаёт быть значительным уже при двух активных сокетах. Оверхед проявляет сильно только при одном сокете. Во всех остальных случаях ивентлуп на одном ядре должен быть быстрее N потоков на одном ядре.

                Чтобы сранивать производительность blocking / non-blocking
                Речь не про blocking/non-blocking сокеты, а про асинхронный и многопоточный код (хоть эти понятия и «ходят рядом»). Не блокирующие сокеты могут и в многопоточном использоваться, например в том же uwsgi вполне возможно используются не блокирующие сокеты до передачи данных в поток, или библиотеки которые работают с пачкой сокетов (например zmq poller).
                • 0
                  В вашем тесте есть одно но: tornado обработывает соединения и парсит запрос с python коде, а uwsgi в python коде тупо дергает одну функцию. Это уже C/C++ vs Python а не threads vs event loop. Тогда надо сравнивать uwsgi (threaded) с nginx(event loop)+wsgi.
                • 0
                  Я про этот «оверхед» выше и написал в отличие от многопоточных фреймворков:

                  Увольте! Я в своём предложении написал про диспетчер и парсер, они есть как в синхронном/блокирующем коде, так и в асинхронном.

                  Вы же, сравниваете два разных фреймворка и судите о производительности синхронности/асинхронности по производительности разных фреймворков и это не правильно.

                  Ваши тесты показывают лишь то, что один фреймворк выдерживает большую пропускную способность чем другой.
                  Но это не значит что у tornado меньшая производительности из-за eventloopа и асинхронности. Она может быть меньше из-за более тяжелого парсера, из-за более тяжелого диспетчера, из-за многих причин. uwsgi же написан на плюсах и в питоне лишь биндинги к сишным вызовам. Конечно он быстрее питонячьего большого tornado!

                  Сравнивать blocking/non-blocking и sync/async надо в рамках одной абстракции, одного фреймворка и одного api.
                  • 0
                    Я в своём предложении написал про диспетчер и парсер, они есть как в синхронном/блокирующем коде, так и в асинхронном.
                    Ок, вот пример с bottlepy — 4356 rps против 2500 rps у торнадо, вариант с WebOb выдает 5000 rps.

                    Вы же, сравниваете два разных фреймворка
                    Все правильно, сравнить надо вещи которые отличаются.
                    судите о производительности синхронности/асинхронности по производительности разных фреймворков и это не правильно.
                    Нет, я просто показал что приложение на асинхронном фреймворке не всегда быстрее, «все зависит от задачи».

                    Сравнивать blocking/non-blocking и sync/async надо в рамках одной абстракции, одного фреймворка и одного api.
                    Приведите свои примеры/тесты.

                    Похоже я понял в чем спор — вы вышли за пределы топика и рассуждаете об абстрактной синхронности/асинхронности, когда я говорю о синхронных/асинхронных релизациях на питоне, с чего и начался тред, конкретно по теме мне было интересно добавить bottle/flask в тесты к tornado, twisted, gevent, хотя это итак видно по моему первому комментарию.

                    Думаю теперь нет смысла у вас спрашивать о наличии «асинхронного оверхеда» в tornado/twisted перед bottleby и аналогами, т.к. это очевидно.
                    Но это не значит что асинхронные фреймворки хуже/медленнее. В первую очередь они нужны для задач которые не под силу (либо не оправдано использовать) потоковым фреймворкам, а бо`льшая производительность получается за счет того что работа не простаивает в ожидании IO операций.
                    И опять же не нужно их пихать везде, даже если асинхронный фреймворк дает лучшую производительность в данной задаче, это не значит что это лучше для проекта в целом.

                    А если говорить об абстрактной синхронности/асинхронности — то это большой философский вопрос, который мне не интересен.
                    • 0
                      В исходном сообщении треда вы написали что у инвент-луп обработки есть оверхед, и я сказал что он незначительный и проявляется в одном случае. Это утверждение выглядело более чем абстрактным.

                      По поводу своих тестов. Я как-то уже давно писал две реализации модулей стрелялки для jmeter. Написал используя голые сокеты (никаких #netty/#grizzly) и получил следующие результаты на тестировании между двумя машинками в разных ДЦ с latency в 5ms. Тестировал echo-протокол. Сокет получил запрос echo, и его же отправил. Пока сокет ответ не получил, следующий запрос он не отправляет.

                      В случае блокирующих сокетов, на каждое соединение выделялось по одному нативном потоку. В случае неблокирующи сокетов количество отдельных потоков для селекторов было малое. Так, для 550Krps было всего 4 потока event-loop'а. Дополнительную информацию можно увидеть тут

                      Так выглядела пропускная способность:


                      А вот так утилизация процессора:


                      Машинки стояли мощные, 32 логических ядра:)
                      На таких графиках видно как работа с неблокирующими сокетами позволяет добиться вдвое большей пропускной способности при той же утилизации. Причем, в случае неблокирующих сокетов, дальше я упёрся во внутренний код стрелялки, который занимается логикой, и дальнейшие бенчмарки без jmeter показали еще большие значения производительности.
        • 0
          У gevent используются корутины, и по факту это обертка над неблокирующими сокетами, и внутри используется select/poll/epoll ;-)
          > Fast event loop based on libev (epoll on Linux, kqueue on FreeBSD). © www.gevent.org
  • +3
    А исходников не будет?

    Сервера уже повыключали? Я бы мог вам на Erlang для сравнения написать аналог.
    • 0
      Только что добавил в конец статьи, в раздел «фреймворки».
    • 0
      Да, к сожалению, тесты проводил несколько месяцев назад, сравнить с Erlang мне было бы очень интересно. В любом случае, спасибо за предложение.
  • +1
    А почему у всех Python 2, а у Tornado Python 3? По памяти, по моим тестам около 10% разница получалась в пользу 2.
    • 0
      Тут важен процессор, по памяти расход везде небольшой. Я бы с радостью везде использовал 3-ий Python, но поддержки нет, это конечно печально.
      • +1
        Стоило проверять все на одной версии питона — они слишком разные по производительности.

        Но, в среднем, питоновский код медленнее на тройке. Например Django медленнее где-то на 7%. Шаблонизаторы еще медленнее — до 20%.
      • +1
        Стоит добавить, что для второго питона есть PyPy и торнадо на нем отлично работает. В отличие от gevent. И разница в производительности там существенная.
      • 0
        Прошу прощения, что ввел в заблуждение. «По памяти» -> «Если ничего не путаю». Разница именно в rps на легкой нагрузке(запись в dict значения).
        Сравнение всё же было бы честнее проводить на одной вресии Python.
      • +3
        Для наглядности прогон ab -c 500 -n 50000 -s 5 127.0.0.1:8888/ на hello world с www.tornadoweb.org/en/stable/
        Тест проведен наспех на попавшемся под руку AMD E350. Python полностью съел одно ядро. Еще одного с запасом хватило на ab и прочее.
        Тяжелые приложения были все убиты. Тест повторялся несколько раз, результаты отличались незначительно.
        Python 2.7.7
        Server Software: TornadoServer/3.2.2
        Server Hostname: 127.0.0.1
        Server Port: 8888

        Document Path: /
        Document Length: 12 bytes

        Concurrency Level: 500
        Time taken for tests: 78.771 seconds
        Complete requests: 50000
        Failed requests: 0
        Total transferred: 10350000 bytes
        HTML transferred: 600000 bytes
        Requests per second: 634.75 [#/sec] (mean)
        Time per request: 787.714 [ms] (mean)
        Time per request: 1.575 [ms] (mean, across all concurrent requests)
        Transfer rate: 128.31 [Kbytes/sec] received

        Connection Times (ms)
        min mean[±sd] median max
        Connect: 0 554 1232.9 0 7027
        Processing: 23 219 86.6 205 5960
        Waiting: 23 219 86.6 205 5960
        Total: 47 773 1240.9 206 7256

        Percentage of the requests served within a certain time (ms)
        50% 206
        66% 223
        75% 251
        80% 1226
        90% 3217
        95% 3233
        98% 3255
        99% 7220
        100% 7256 (longest request)

        Python 3.4.1
        Server Software: TornadoServer/3.2.2
        Server Hostname: 127.0.0.1
        Server Port: 8888

        Document Path: /
        Document Length: 12 bytes

        Concurrency Level: 500
        Time taken for tests: 101.091 seconds
        Complete requests: 50000
        Failed requests: 0
        Total transferred: 10350000 bytes
        HTML transferred: 600000 bytes
        Requests per second: 494.60 [#/sec] (mean)
        Time per request: 1010.911 [ms] (mean)
        Time per request: 2.022 [ms] (mean, across all concurrent requests)
        Transfer rate: 99.98 [Kbytes/sec] received

        Connection Times (ms)
        min mean[±sd] median max
        Connect: 0 696 1161.8 0 15040
        Processing: 51 304 50.9 295 2005
        Waiting: 50 303 50.9 295 2005
        Total: 72 999 1162.7 331 15301

        Percentage of the requests served within a certain time (ms)
        50% 331
        66% 1282
        75% 1316
        80% 1335
        90% 3282
        95% 3303
        98% 3346
        99% 3391
        100% 15301 (longest request)

        Краткий итог:
        Python 3 ~495 rps, 1011 ms mean, total time 101 s
        Python 2 ~635 rps, 788 ms mean, total time 79 s
        Итого Tornado на Python 2 в данных условиях почти на 30% быстрее, чем на Python 3.
        P.S. Тест только для наглядной демонстрации заметной разницы. На реальных задачах такой разницы не наблюдал, но 10% легко.
        • 0
          Большое спасибо за ваш тест. Теперь я понял, что вы имели ввиду. Ниже habrahabr.ru/post/228455/#comment_7741325 я ответил почему использовал разные версии Python во время тестирования.
  • +4
    еще бы Go lang потестировать по той же схеме…
    • 0
      На Raspberry Pi:) и в один поток, Go оказался быстрей node.js но медленней nginx — gist.github.com/msoap/7060974
  • +2
    эмм. а почему в тесте руби 1.8.7? сейчас во всю идет 2.1.2.
    Еще, не могли бы вы объяснить, почему вы тестировали разные фреймворки на разных версиях python?
    • +3
      Я использовал каждый фреймворк с последний версией Python, которую он поддерживает. Я бы не стал использовать 2-ую версию Python в новом проекте даже в случае более высокой производительности. Другими словами, я не вижу смысла использовать Tornado со 2-ой версией Python в то время, когда он поддерживает 3-юю. С версией ruby вы правы, я проглядел, виной тому стабильный дистрибутив Debian, я думаю.
      • 0
        Ну тут уж либо шашечки, либо ехать. Если Вы не хотите писать на Python 2(я давно не писал на 2 и уж точно стану без весских причин возращаться) — тогда нет смысла вообще смотреть на фреймворки, которые не поддерживают Python 3. Если же это в принципе рассматривается, то сравнивать надо на Python 2. Разница может быть больше, чем между фреймворками. Так что во многом смысл сравнения теряется.
        • 0
          Я очень не хотел писать на Python 2 и это было почти невероятным событием для меня. Но я думал так — если я найду какое-то решение, которое будет уходить в отрыв и будет поддерживать только Python 2, то это станет поводом копать в сторону этого решения.

          Так стало как раз с Gevent, который официально не поддерживает 3-ий Python, но есть форк, который поддерживает. Я бы, честно говоря, и использовал сейчас именно этот форк, если бы не реализация asyncio в python 3.4. Вы можете сказать, что я должен был сравнить производительность этого форка Gevent с тем же Tornado на 3-их версиях и я с вами соглашусь.Трудность здесь, как я написал в статье, с выделенным количеством времени на мое исследование. Для меня это мог стать бесконечный процесс, который закончился бы каким-нибудь Erlang'ом.

          Если бы я тогда последовал принципу проверить все на Python 2, что бы сравнение между фреймворками было более честным, и выиграл бы Tornado или любой другой фреймворк, который поддерживает Python 3, то я бы безусловно захотел использовать именно 3-юю версию с ним, а тогда мне пришлось бы все равно запускать тесты еще и на 3-ей версии Python.
      • 0
        В Ruby с пришествием 1.9 (а сейчас уже 2.1) всё должно стать заметно лучше, как минимум по памяти.
        Там появились Fibers (легковесные быстросоздаваемые треды с их шедулингом силами приложения, а не VM). Ну и Garbage collector с тех пор начительно улучшился, как по скорости так и по поведению Copy-on-Write механизмов.

        Added: Это я к тому, что комментаторы не зря указывают автору на устаревшую версию Руби.
        • 0
          Большое спасибо за информацию. Я обязательно почитаю про Fibers, что бы сравнить производительность с Asyncio в Python 3.4, в будущем.
  • +1
    Ммм, 1.8.7, некрофилия…
  • 0
    Хотелось бы увидеть конфиг nginx, так же узнать какой был tcp congestion control algorithm и другие настройки tcp включая initcwnd, initrwnd.
    • +1
      Я, к сожаленю, уже не смогу получить эту информацию. Исследование я проводил несколько месяцев назад, машин в наличии уже нет, на которых я проводил тест. В любом случае спасибо за вопрос, когда я решу делать еще одно тестирование, я буду знать какую информацию стоит добавить в статью.
  • 0
    А почему у Вас GridFS только для 2-х серверов протестирован?
    • 0
      К этому моменту я уже решил, что буду использовать Gevent в своем проекте (в результате получилось не так), а свои сроки на тестирование я уже привысил, поэтому я решил провести сравнение с одним из самых распространенных решений node.js + mongodb.
      • 0
        >… использовать Gevent в своем проекте (в результате получилось не так)...
        И что же вы в результате стали использовать?
        • +2
          Asyncio
          • 0
            И это правильно!
  • +2
    Asyncio из Python 3.4

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.