Golang vs python benchmark

MayankFawkes / benchmark.md

Python and Go benchmark, i will be using using flask and fastapi for python with gunicorn server and for go built in package. I will be using hey for HTTP load generating.

package main import ( "fmt" "net/http" ) func main() < http.HandleFunc("/", HelloServer) http.ListenAndServe(":8000", nil) > func HelloServer(w http.ResponseWriter, r *http.Request) < fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) >
go build -o api api.go && ./api 

Load test with 250 concurrent connections and 5000 total requests

root@mayank:~/golang# ./hey -c 250 -n 5000 Summary: Total: 2.9152 secs Slowest: 1.3516 secs Fastest: 0.0384 secs Average: 0.0917 secs Requests/sec: 1715.1451 Total data: 40000 bytes Size/request: 8 bytes Response time histogram: 0.038 [1] | 0.170 [4363] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.301 [120] |■ 0.432 [434] |■■■■ 0.564 [12] | 0.695 [56] |■ 0.826 [1] | 0.958 [2] | 1.089 [3] | 1.220 [0] | 1.352 [8] | Latency distribution: 10% in 0.0401 secs 25% in 0.0409 secs 50% in 0.0435 secs 75% in 0.0651 secs 90% in 0.3042 secs 95% in 0.3440 secs 99% in 0.6570 secs Details (average, fastest, slowest): DNS+dialup: 0.0016 secs, 0.0384 secs, 1.3516 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs req write: 0.0000 secs, 0.0000 secs, 0.0006 secs resp wait: 0.0861 secs, 0.0384 secs, 1.3324 secs resp read: 0.0000 secs, 0.0000 secs, 0.0012 secs Status code distribution: [200] 5000 responses 
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, World!'

Starting gunicorn server with 1 thread and 3 workers ((2*CPU)+1)

gunicorn --workers=3 --threads=1 -b :80 api:app 

Load test with 250 concurrent connections and 5000 total requests

root@mayank:~/python-flask# ./hey -c 250 -n 5000 Summary: Total: 12.7446 secs Slowest: 4.3519 secs Fastest: 0.0780 secs Average: 0.5286 secs Requests/sec: 392.3238 Total data: 65000 bytes Size/request: 13 bytes Response time histogram: 0.078 [1] | 0.505 [3901] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.933 [224] |■■ 1.360 [491] |■■■■■ 1.788 [315] |■■■ 2.215 [0] | 2.642 [2] | 3.070 [0] | 3.497 [55] |■ 3.924 [4] | 4.352 [7] | Latency distribution: 10% in 0.1143 secs 25% in 0.2048 secs 50% in 0.4065 secs 75% in 0.4622 secs 90% in 1.3421 secs 95% in 1.3787 secs 99% in 3.1049 secs Details (average, fastest, slowest): DNS+dialup: 0.2643 secs, 0.0780 secs, 4.3519 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs req write: 0.0000 secs, 0.0000 secs, 0.0005 secs resp wait: 0.2639 secs, 0.0390 secs, 2.3810 secs resp read: 0.0002 secs, 0.0000 secs, 0.0767 secs Status code distribution: [200] 5000 responses 
fastapi==0.92.0 gunicorn==20.1.0 uvicorn==0.20.0 
from fastapi import FastAPI app = FastAPI() @app.get("/") async def root(): return "message": "Hello World">

Starting gunicorn server with 1 thread and 3 workers ((2*CPU)+1)

gunicorn --workers=3 --threads=1 -k uvicorn.workers.UvicornH11Worker -b :80 api:app 

Load test with 250 concurrent connections and 5000 total requests

root@mayank:~/python-fastapi# ./hey -c 250 -n 5000 Summary: Total: 5.3730 secs Slowest: 4.3693 secs Fastest: 0.0389 secs Average: 0.1493 secs Requests/sec: 930.5720 Total data: 125000 bytes Size/request: 25 bytes Response time histogram: 0.039 [1] | 0.472 [4717] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 0.905 [171] |■ 1.338 [67] |■ 1.771 [22] | 2.204 [13] | 2.637 [6] | 3.070 [1] | 3.503 [1] | 3.936 [0] | 4.369 [1] | Latency distribution: 10% in 0.0412 secs 25% in 0.0424 secs 50% in 0.0475 secs 75% in 0.1402 secs 90% in 0.3963 secs 95% in 0.5025 secs 99% in 1.2908 secs Details (average, fastest, slowest): DNS+dialup: 0.0020 secs, 0.0389 secs, 4.3693 secs DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0000 secs req write: 0.0000 secs, 0.0000 secs, 0.0006 secs resp wait: 0.1411 secs, 0.0388 secs, 4.3570 secs resp read: 0.0023 secs, 0.0000 secs, 0.8092 secs Status code distribution: [200] 5000 responses 

Источник

Читайте также:  Private method in interface java

Python vs Golang — cравнение быстродействия

Вот у меня есть две программы с совершенно одинаковым функционалом – одна на языке Python, вторая – на языке Go, обе не используют возможности параллельных вычислений. Проведу тестирование для сравнения их быстродействия, но сначала небольшое описание их работы.

Они ищут некоторые заданные комбинации планет в списке гороскопов однотипных событий, а подробнее следующее:

  1. Загружает в таблицу эфемериды (координаты планет) из текстового файла.
  2. На указанную дату из списка событий определяет положения планет и куспидов домов, то есть рассчитывает гороскоп, создается массив координат планет и куспидов.
  3. Рассчитывает аспекты (угловые расстояния) между планетами, а также главными куспидами (Асцендентом и Меридианом) — создает массив аспектов.
  4. В этом массиве производится поиск сочетаний планет, например самый точный аспект должен быть 120 градусов между Луной и Юпитером, а самый точный аспект Углов — Асцендент в 90 град с Марсом.
  5. На экран выводится результат поиска.
  6. Так же можно искать подобные сочетания в указанном диапазоне времени.

Для тестирования сравним следующие варианты запуска программ: через командную строку, для Питона и Go (go run poiskformul.go), Питон через оболочку IDLE shell, скомпилированная программа Go (.exe) . Все происходит на одной и той же машине. Программа производит поиск в списке 277 событий.

Первый тест – поиск заданной комбинации планет в списке событий. На экран выводятся только найденные комбинации, в конце показывает количество встреченных тех или иных планет.

Второй тест – то же, что в первом + в каждом цикле на экран выводится список 20 аспектов.

Таблица. Продолжительность выполнения тестов, время в миллисекундах

Примечание — повторный запуск «go run poiskformul.go» производится в том же терминале cmd без закрытия/открытия, после 3 запуска скорость резко возрастает.

Читайте также:  Кнопка включения в html

Получается, что программа на Go работает быстрее, чем на Python — в худшем варианте в 3,8 раза, в лучшем — в 404 раза, в наиболее типичных вариантах использования — примерно в 300 раз.

Эта программа мне потребовалась для моего исследования, связанного с астрологией. Поскольку я не был программистом, а программа мне была нужна, то сначала я интересовался, где можно такую купить или заказать. Потом я решил написать её сам. Когда-то давно я программировал на Basic, а про современные ЯП ничего не знал. Почитал про языки программирования и выбрал Python, который рекламируется как простой и доступный.

Примерно 2 недели я читал книги и делал упражнения из них. А когда эти упражнения стали сложными, я подумал, что поскольку моя цель – написать мою программу, то нет смысла тратить время на выполнение этих сложных заданий. Через 2 недели интенсивного умственного труда программа была готова.

При создании программы мне потребовались формулы для расчета координат куспидов домов. Эти формулы я нашел на сайте https://www.astrologic.ru/denis/houses/07formula.htm и внес в программу. Но оказалось, что они выдают такие результаты, которые нельзя сразу применять. При ручном построении гороскопа человек может интуитивно или на основе предыдущих данных расставить их в правильном порядке, но компьютеру надо объяснить, как это сделать. Пришлось подумать и сделать алгоритм, выполняющий правильную расстановку .

Через какое-то время мне захотелось поднять быстродействие программы, поскольку поиск в большом количестве данных шел довольно долго. Некоторая оптимизация кода помогла, но недостаточно. Я присматривался к параллельным вычислениям, но в итоге решил переписать программу на более быстром языке. После некоторых поисков и обдумывания я пришел к Golang, поскольку мне очень понравились его идеология и назначение.

Читайте также:  Как обозначить пробел в питоне

Конечно, просто так переписать код из Python + Pandas на язык Go не получилось. Первая трудность, с которой я столкнулся – типизация. В Питоне она динамическая, а в Go – строгая статическая. Сложность возникла с таблицами. В моей программе все двумерные массивы были реализованы с помощью DataFrame из Pandas, и в них хранятся данные разных типов (время, строки, числа, списки), а в Gо это невозможно. Пришлось обдумать, как это сделать. Появилось 2 варианта – набор одномерных массивов (каждый из них – столбец со своим типом данных), связанность данных будет обеспечиваться одинаковым индексом либо массив структур. В итоге я сделал оба варианта и проверил их быстродействие – оно оказалось практически одинаковым. Но массив структур мне понравился больше, поэтому я стал применять его.

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

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

В итоге программа на Go заработала и гораздо быстрее, чем код на Питоне.

Источник

Оцените статью