Go vs Python. Виталий Левченко

    Предлагаю ознакомиться с расшифровкой доклада Виталия Левченко Go vs Python


    Go — волшебное слово, решение всех проблем продакшна разом и одновременно негодная технология без эксепшнов. Истина посередине, поэтому поговорим о конкретных примерах:


    • asyncio vs горутины;
    • производительность узких мест;
    • лаконичность vs простота кода;
    • порог входа;
    • тулинг и паттерны поиска проблем и оптимизации производительности;
    • обслуживание в продакшне.



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


    Собственно, почему я это всё делаю? Потому что я продвигаю Go с 2012 года, делаю Go MeetUp’ы с 2014, делаю кучу сервисов на достаточно большие нагрузки.



    То, с чего вообще начинаю сравнение языков – это бенчмарки.


    С бенчмарков вообще принято сравнивать. Если начинать холивар, то говорят: «Вот у нас есть мега бенчмарк, дергаем http, собственно, одно быстрее, другое медленнее». И вот так устроена половина холиваров в интернете уж точно. Мы не будем исключением, возьмём http. На самом деле возьмем просто бенчмарк, который сделали прекрасные люди из techempower.com/benchmarks/. У них http и довольно простенький формат в духе веб-приложения, достать 16 текстов из базы данных, отрендерить и выдать по http.



    Они тестируют на довольно неплохом железе, на 14 ядрах, с гипертрейдингом.



    И выдают следующие цифры. Собственно, 329 000 запросов в секунду переживает Go со специальным модулем для http, без специального модуля на нативном http он переживает 180 000 запросов в секунду. Это больше, чем перемалывают обычные сетевые карты.



    С Python все гораздо грустнее. 65 – 70 тысяч запросов это специализированные фреймворки, для того, чтобы отвечать быстро.



    А если берем django, всё ещё грустнее. Мы видим разницу больше, чем на порядок, то есть в 5 раз разница идёт со специализированным фреймворков, в 5-6 раз идёт разница с http, и django гораздо медленнее, ну и все остальные Twisted и прочее.



    На самом деле имеет смысл сравнить latency, потому что это часто более важно. Я работал в рекламе, в стриминге. Там было важно. Если задержка больше 100 миллисекунд, то пользователи уже начинают жаловаться, что подтормаживает.



    Фишка в том, что Python оказывается вполне с адекватными цифрами с latency, т.е. 7 миллисекунд на ответ на технагрузку это прямо хорошо. Но больше всего меня удивил django.



    Latency у django получилось меньше, чем у Go. Если возьмёте в руки калькулятор и просто разделите одну цифру на другую, то есть вот эти 14 тысяч запросов в секунду, 28 ядер и 2 миллисекунды, оно поделится ровно (2ms 14,000req/s = 28 cores 1). Django отвечает без накладных расходов. Я к тому, что на самом деле все не так плохо, как показалось с самого начала.



    Следующий момент, который сравнивается в бенчмарках, те люди, которые более-менее прошаренные, как работают приложения, это аллокации. Собственно, в чём суть? Нам периодически из кэша надо доставать кучу данных, например из redis или базы данных. Скажем, мы достаем 10 000 объектов по 100 полей, такие обычные ситуации, то есть, скажем, 10 раз по 1000 объектов достали и как-то их пережевали. В json это какие-то смешные 20 мегабайт, а то и 10 мегабайт.



    Мы достаем из redis данные за долю миллисекунды, сеть их приняла за долю миллисекунды, а после этого Python их переживал ещё 200 миллисекунд. Осознайте эту дельту в цифрах. Больше того, после этого обработать эти данные будет стоить 10 миллисекунд в худшем случае. Аллокация – это реально дорого. Но фишка в том, что Go не сильно быстрее, но в Go есть другая штука, называется «кэш в памяти», мы можем просто этот кэш засунуть в память, не дергать его из базы, не дергать его из редиса, а просто брать и использовать.



    Но мы можем попробовать использовать multiprocessing в Python. Дальше увидеть то, что обновление этого объекта в памяти, это 2 секунды, в течение которых приложение не может доставать из кэша ничего. То есть каждое обновление элемента кэша — это фриз приложения на 2 секунды. И вот это вот, это не в тупую запись, это просто перезапись объекта, то есть dict-update или что-то в таком духе.



    Поэтому мы можем память не шарить, а просто держать ее в памяти локального приложения. Но дальше мы получаем очень грустную историю, если нам объектов нужно хранить много, скажем, 10 Гб в целом приложении, то мы получаем грустную историю, нам на 28 ядер нужно 280 Гб памяти. И это нерешабельная проблема в Python. Если вам по каким-то причинам нужно перемалывать хоть сколько-то значимое количество объектов, то Python просто не про вас.



    И еще один важный момент: если вы всё-таки делаете 28 приложений, то есть вы запустили multiprocessing и крутите. Вы хотите сделать общие счётчики, например, метрики считать, просто какие-то счётчики. Выясняется, что сохранять в redis будет стоить 0,2 миллисекунды на каждый запрос. В случае Manager.Value плюс-минус такие же цифры, т.е. если мы в общую память сохраняем. Плагин Prometheus для счётчиков используют mmap и merged их на живую. Я не до конца понимаю, почему это работает и не падает по дороге. То есть он просто берёт на каждый процесс mmap файл и после этого без всяких mutex просто берет и merged. Это выглядит страшно, но по виду работает.



    В Python в 2018 году появилась нормальная асинхронность Async/Await.



    Логика вот какая. Я веду мысль к тому, что есть кейс, с которого я начинал знакомство с Go. Он выглядел примерно следующим образом, у нас были просто очереди в TopFace, они перемалывают какие-то лайки, запросы, сообщения, обработку подарочков. Технически это просто очереди, которые перемалывают демоны. Они упираются в кучу запросов в базу, в какую-то арифметику на этих данных и ЦПУ.


    Представим, что у нас на машинке те самые 14 ядер. Как вы думаете, сколько воркеров надо запустить, чтобы эффективно утилизировать ЦПУ?


    На самом деле, действительно, зависит от ситуации. Реальная цифра порядка 100-150-200, в зависимости от того, насколько быстро базы отвечают, и это в случае, если мы не используем нормальные goroutine и асинхронность. Больше того, если у нас база начинает отвечать медленнее, у нас ЦПУ не догружено, если база отвечает быстрее, у нас процессор оказывается перегружен на 200 потоках и машинка перестает отвечать хоть как-то. Поэтому нужны goroutine, поэтому нужно эффективное использование каждого ядра в таком формате.


    Почему это важны goroutine в этом контексте? Мы можем запустить 200 потоков, которые просто что-то делают, а можем запустить 100 потоков, а можем 14, и по-простому что они делают во время запроса в базу? Если у нас goroutine, во время запроса в базу, она обрабатывает другие потоки, если у нас не goroutine, то просто thread залочен и все. Поэтому в случае goroutine мы хоть как-то это утилизируем. Этим Go крут. Потому что мы просто запускаем приложение. Оно просто скушает все ЦПУ. Поэтому переходите на 3.7, если у вас многопоточное приложение и вы упираетесь в ЦПУ.



    В принципе по асинхронности самое важное, что есть в Go, это обработка в scheduler. Она сделана хитро. То есть просто syscall при каждом вызове отдают goroutine в scheduler с пометкой, что она не занимается ничем полезным, и возвращает из scheduler, когда syscall заканчивается. У нас в контексте 14 ядер, будет запущено ровно 14 goroutine, которые не в syscall, ну или меньше, если ядер физически меньше. Это работает, причем работает магический круто, т.е. вы просто запихнули какие-то goroutine, они что-то делают, они включаются в базу, на диск, еще что-то, ЦПУ утилизируется нормально, и ничего не блокируется, потому что все блокировки, они в первом пункте.


    С mutex аналогично, но поскольку они в нативной библиотеке, стандартной, все проработано и точно так же работает.



    На самом деле в Asyncio всё похоже. Точно также, если идёт запрос в сеть, он просто передается в Scheduler, который разруливает ее с какими-нибудь kqueue, epoll. Это красиво работает, это относительно синхронно на текущий момент выглядит, не так, как у Go. Но с Async/Await это хоть как-то хорошо.



    Беда в том, что не все функции не блокирующие. Это приводит к грустной истории. Вот мы описали честный поток Async/Await. А потом внутри случайно вызвали функцию, которая что-то блокирует. Чем больше приложений, тем больше вероятность, что мы такую функцию вызовем. У нас весь runtime встал целиком, пока функция не разблокируется.



    Поэтому нужно использовать внешние библиотеки, которые Asyncio точно поддерживает. Мы открываем aio-libs, которые пишут 11 баз данных. У них, правда, есть всякие интересные проблемы, например оно падает на каких-то хитрых ответах или при попытке сделать Close, но оно делает вид что работает. Там нет важных для production баз типа Aerospike, и на самом деле там не хватает ряда полезных таймаутов. Это проблема. Если у вас есть какой-то таймаут базы, который вы не можете поставить, и этот таймаут сработает, то у вас горутина сдохнет на время таймаута, и вам нужно либо снаружи обрабатывать каким-то странным образом.



    В Python нет нормального способа коммуникации между thread. В Go это есть из коробки. Вот за это реально надо любить Go. Мы можем честно взять и уведомить все goroutine, в которых происходят какая-то обработка, и сказать им: «Надо завершиться». Например, мы делаем Graceful shutdown. Мы шлем сигнал, все аккуратненько завершаются и после этого мы останавливаем приложение. В случае Python нужны либо поддержки соответствующих обработчиков, то есть поддержка внутри http, внутри net и других библиотек. И всё равно это неудобно. Вы всё равно пробрасываете огромное количество каких-то вещей. У вас нет гарантий, что он завершится. А в Go здесь прям просто, из коробки, вы просто делаете канал, в который вы шлете сигнал о том, что приложение завершается и обработчик завершается.



    Больше того, Select в Go еще крут тем, что мы хотим читать сразу из 5 разных потоков. Например, у нас есть 5 разных продюсеров, они пикируют 5 разных видов данных, их надо обрабатывать по-разному, мы их можем просто взять и обрабатывать всех по-разному. В случае Python нам их нужно объединять в единый queue, то есть сперва объединить с потерей типов, потом обратно разъединить с нахождением типов, потом узнать, что нужно читать в двух разных местах и всё сломается. В Go такого нет, и это круто.



    Теперь перейдем к самой, наверное, холиварной вещи в контексте Python и Go. Люди говорят: «Я пишу в DataScience, я всякие модельки рассчитываю, умножаю их и так далее».



    На самом деле DataScience, это здорово, и за него хорошо платят, поэтому заниматься DataScience это здорово.



    Но в Go на самом деле все не так уж и плохо. То есть более-менее все популярные библиотеки сделано и сделано нормально. Больше того, если вы в какой-то момент начинаете упираться в ваши библиотеки, например, у вас обучение работает слишком медленно или оно вам нужно в других параметрах, или вам нужно онлайн-обучение, сразу выясняется, что ничего готового нет, надо делать самим.


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



    А если напряглись, то получилось сильно быстрее. Есть пример, SсiKit — расчёт рекомендаций. Это то, что называется коллаборативной фильтрацией, когда у нас есть пользователь, который фильмы лайкает. Мы хотим найти похожих пользователей. Исходя из этого оценить для пользователя, как он оценит текущий фильм. Есть Sсikit. Есть Surprise библиотека, которая умеет SVD. Там есть 2 алгоритма, SVD, который обсчитывает модельку 2 минуты и SVD++, который обсчитывает эту модельку, конкретную, за 3 часа. Как вы думаете, за сколько Go считает SVD++?



    На самом деле 2 минуты. Если вы берете библиотеку, которая хорошая, уровня enterprise, она много где используется, вы можете в ряде случаев увеличить производительность на два порядка. Если вам хочется улучшить текущую производительность там, вы можете просто взять библиотеку с поддержкой AVX2 и получить прирост ещё на 20% для обычного SVD. У меня есть знакомая, которая, собственно, использует Go именно для конкретного обучения, просто взяли и переписали нужную генерацию из Spark на Go. Это стоило им в разработке пары недель.



    И тут мы подходим к тому, ради чего с Go хорошо делать, когда мы говорим про производительность, это всяческая оптимизация. Мы берём Go, пишем неоптимизированный код. Если мы хотим, чтобы оно стало работать еще быстрее, мы просто вызываем профилирование. И оно начинает работать в 2 раза быстрее или в 10 раз быстрее, если мы что-то не так сделали.



    Профайлинг делается в Go в одну строчку. Просто добавляем импорт pprof и он выкидывает данные наружу по http. Эту ссылочку можно открывать прямо в браузере или через нативную утилиту pprof.



    Она показывает, что в конкретных либах, в том числе системных, в том числе какие-то syscall тормозят. Она говорит: «Тормозит Garbage Collector» или «Тормозит конкретный mutex», или «Тормозит конкретная ваша логика».


    Прямо из этого pprof можно получить list func. Он построчно покажет на каких строчках она сколько времени проводит.


    Дальше мы можем вызвать disasm этой функции и ещё увидеть, на каких ассемблерных строчках оно так себя ведет.



    Общий профайлинг выглядит примерно вот так. Это просто визуализация времени вызова.



    Есть FlameGraph, который внезапно в Python появился раньше.



    И вот то, что я говорил, можно построчно показать, как это по миллисекундам работает.



    Мы можем взять флаг gcflag -S и получить исполняемый ассемблерный код прямо на живую.


    Можем поставить gcflag -m при компиляции и после этого нам pprof будет выдавать информацию по Inline функциям локации на хипе.


    Мы можем взять и переписать функцию на C или Ассемблере. После этого понимая, что происходит. Например, мы видим какую-нибудь функцию, которая работает 8% времени, мы просто берем и переписываем и 8% времени превращается в 10% времени.


    Мы можем сравнить 2 профайлинга. Крутость Go в том, что на этом основана разработка Go как такового. То есть вот разработчики реально каждый раз приходят и говорят: «У нас раньше было http c такой скоростью работала, вот в этой версии с такой скоростью», прямо циферки показывают. И это каждый раз, это уникально из того, что я видел по языкам, это именно когда разработчики всерьёз заморочены и отчитываться по оптимизации.



    Pprof умеет умеет показывать информацию про память, блокировки, mutex, stacktrace, tracing живой. Например, вы запустили и на живую можете посмотреть, как это работало последние 5 секунд. Можете даже отслеживать, что вы из собственного треда запускаете, если вы по каким-то причинам запускаете много, это можно отловить.



    Есть build-race — актуален, как только вы начинаете работать во много потоков. Например, вы просто пытаетесь в одну и ту же переменную писать параллельно, или писать и считать. Build-race соберет приложение с поиском race conditions, он будет работать в 4 раза медленнее, но при этом он вам отчитается, что вы работаете с блокировками не так или просто работаете с памятью не так.


    Можно еще тестировать память, можно тестировать сколько локаций происходит и так далее, это полезно, когда вы начинаете работать с GC.



    На самой деле самое интересное, это разработка.



    Крутость Go в том виде, в котором я с ним работал, это то, что мы апгрейдим Go, вот реально с 1.0 на 1.1, с 1.1 на 1.2, с 1.2 на 1.3 и так далее, с 1.11 на 1.12, и у вас он просто работает, то есть работает ваш код, работают все библиотеки, без единой адаптации. Это было у меня с 2012 года.


    И вторая вещь, которая крутая. Она спорная, потому что долгое время не было нормальной работы с зависимостями. Когда мы делаем go get и библиотека в Go достается из мастера и он летит сразу всем разработчикам, которые используют эту библиотеку. Это минус. Но в связи с этим, мэйнтенеры библиотек дисциплинированы. Они знают, что если они что-то поменяют обратно не совместимо, то существенная доля разработчиков будет сильно недовольна, причём очень сильно. Если первые годы это было неожиданно, то сейчас это стало привычкой.


    Если делается обратная несовместимость, то это делают другой пакет/репозиторий. И оно так работает. Оно так прижилось. Реально обратную совместимость не портят, за очень редким исключением.



    На моем пути, вот прям в живом соседнем проекте, взяли Python, обновили с 3.6 на 3.7 и он не скомпилировался. Написал, что какие-то QT библиотеки нормально не работают.


    Берём библиотеки Python и выясняем, что мажорная версия ломает обратную совместимость, причем ломает так, что это чинить неоправданно дорого.


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



    В Go с зависимостям всё даже повеселее, чем в Python, потому что не принято зависеть от системных либ, принято, если вы что-то используете в системных либах, принято это писать самостоятельно.


    Я вижу скептические лица, но на самом деле это хорошая идея. Потому что после этого оно переносимо. Если библиотека заявила, что она поддерживается на Mac, Windows, Linux и еще на каком-то телефоне, то она там действительно поддерживается. И не надо доставлять 5 библиотек непонятных версий.


    В версии Golang 1.12 появился Go Mod именно как дефолтный инструмент для работы с зависимостями. Всё стало здорово. Мы просто вызываем go install. Он сам просто заполняет файл go.mod при вызове go get. Вам не нужно делать pip freeze и прочее. Нет таких проблем, если забыть сделать pip freeze.


    Зависимости по умолчанию не хранятся вместе с пакетом. Нет директории аналогичной virtualenv, в которой лежит половина интернета, как в случае с какими-нибудь node_modules. В Python этого поменьше, но всё равно такие существенные директории получаются, если много чего используете.


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


    В Go библиотеки не пытаются что-то делать с runtime: GC паузы перенастраивать, так, чтобы GC вызвался или, наоборот, GC не вызывался, или пропатчить какие-то функции системные. Такого ничего нет.



    Своя библиотека в Go. Вы просто берете свою директорию, копируете в соседнюю директорию, там её инициализируете, заносите прямо в гитхаб и она сразу работает. Больше сразу на godoc.org у вас будет документация к вашему проекту прямо из коробки.


    Если вы, например, патчите чужую библиотеку, у вас PR приняли, и вы сразу можете пользоваться результатами. Вам смержили PR минуту назад, вы уже через go install получаете пропатченную зависимость.


    Если вам по каким-то причинам это не нравится. Например, мержат медленно или ещё что-то, вы просто берете и используете свой форк, что делается реально в одну команду.



    Раз уж мы заговорили про удобства, то прикольно в Go, это то, что есть документация.



    Вы вызываете godoc -http=:6060 и у вас прям вся документация по Go и всем пакетам прямо в ноутбуке и работает в режиме самолета. Я реально в самолёте писал код. Система документации реально одна, то есть нет 10 способов, как это делать через какой-нибудь pydoc или sphinx. У нас godoc, он стандартный, он везде работает.



    Теперь идём к самому интересному. Кто из вас слышал, что в Go нет Exception? Так вот это неправда, они там есть и называются panic.



    Есть прекрасный кейс от Никиты Соболева, о том, что просто берем функцию, которая не знаю, дергает интернет, спрашивает пользователя и пытается вернуть этого самого пользователя наружу. Как вы думаете, в скольких местах это может сломаться с Exception? Во многих. И очень хорошо бы Excepton обработать, потому что они разные. Их нужно наверх пробросить с разными кодами и так далее. В идеале ещё и как-то перемолоть по-разному.



    На Go это просто делается выводом ошибок. Функции, которые могут вывести ошибку, они последним параметром выводят ошибку и мы можем ее обработать. Делаем http get. Если вернулась ошибка, то возвращаем ошибку наверх, с какой-нибудь оберткой.



    Следующий пример. Обработка статус кода, который внезапно не таймаут. Нам сервер ответил корректно 500 http code. Это меня каждый раз удивляет в http либах. Ответ http code 500 считается корректным, а 400 это точно корректный. Ну и собственно дальше, декодировать и обратно вывести. Вместо неявного Excepton у нас появились явные ошибки, по которым мы хотя бы видим, что сломалось и в каком месте.



    Но здесь же можно внимательно подумать и обработать это. Например, не вернуть ошибку и сказать, если внешний сервер лежит, то у нас нет пользователя.



    Кодировки. Это забавная штука, в Go сразу UTF-8, а в Python с 3.7 тоже сразу UTF-8, но на практике не все утилиты/библиотеки об этом догадываются.



    Если вы опытный бэкендер, вам без разницы на чём писать, на Python, Ruby, elixir, Go, Java. Если это не C++, то точно на чём угодно.



    Есть гораздо более важный показатель – зарплата. Да, мы берём статистику "Мой круг" за вторую половину 2018 года. Они говорят, что 75 процентиль зарплаты разработчика Python 150 тысяч рублей, 90% процентиль – 185 тысяч рублей. У "Get It" есть бот в телеграме. Они говорят, что Senior разработчик Python получает 175 – 200 тысяч рублей.


    StackOverFlow, который тоже собирал статистику по 100 тысяч пользователей по всему миру, они говорят 98 тысяч долларов за Python.



    С Go все куда веселее. Собственно "Мой круг" дают 180 – 225 тысяч рублей, "Get It" дает 205 – 250 тысяч рублей, StackOverFlow 110 тысяч долларов в год. В целом + 20% за то, что вы пишете на Go.



    Python лучше, если же вам на самом деле нужно быстро запустить приложение. Если вы делаете в Data science и вас устраивает то, что получается, тоже здорово.



    Если вы упираетесь в скорость ответов, в latency ответов, в память, которую надо хранить или скажем вам нужно с диском аккуратно работать. Внезапно выяснится то, что Go существенно лучше. Например, когда выяснится, что сеть медленная.


    Go хорош, когда вам важна простота operation. Вы просто скопировали/задеплоили бинарник в production и забыли про него. Вы просто обработали все ошибки, поэтому вы знаете какие ошибки есть.


    Почему зарплаты в Go больше? Потому что задачи более сложные. Это более интересные задачи и за них дают больше денег



    Вопрос: Какой интерпретатор питона вы использовали в бенчмарках?


    Ответ: СPython. По первому бенчмарку, который http. Там PyPy использовался. Он не сильно лучше. Где-то хуже, где-то лучше.


    Вопрос: Вопрос относительно скорости разработки на Python и Go, потому что всё-таки Python считается более высокоуровневый и вот были какие-то исследование по данной теме?


    Ответ: Я специально искал такие исследования, я их не нашёл. Я могу поделиться своим опытом по этому вопросу. Внезапно скорость разработки, она ограничивается тем, с какой скоростью мы можем воспринимать текст, который написан, то есть вот скажем 90%, 80% времени мы читаем код, 10% пишем. И здесь более критично не количество букв, а сложность кода. Если вы сделали лямбду лямбду в лямбде. Это читать тяжело.


    Код Go простой. Его тяжело сделать по-разному. И это замечательно. В go действительно реализован единый хороший формат. И поскольку он реализован, вы с первого раза считываете паттерны, а потом просто читаете код с листа быстро. Исходя из этого скорость разработки сравнимая.


    Вопрос: Холиварный вопрос, почему еще не все на Go?


    Ответ: Люди консервативные. Компании еще более консервативные. Если взять рынок, который действительно конкурентный, например, OpenSource. То мы видим что все инфраструктурные утилиты пишут на Go с 2014 года: Prometheus, influxdb, docker, Kubernetes. Все сервисы, которые начинает упираться в вышеуказанные проблемы, например в DropBox или CloudFleer, то они пишут на Go. В OpenSource инфраструктурные утилиты исключительно на Go пишут.


    Вопрос: Ты сказал, что зарплаты самое важное, но если пойти на тот же "Мой круг", окажется, что на Scala и на Elixir платят больше, это значит, что мне надо пойти туда, а не в Go?


    Ответ: В 2018 году Go стал более дорогим. Больше того, есть американский обзор по зарплатам, который говорит, что вот именно из скиллов, по языкам, Go самый дорогой.


    Вопрос: Ты упомянул две вещи, которые мне кажется немножечко взаимоисключающими. Ты сказал про читаемость кода. Любая обработка ошибок эксплицитная. Она замыливает глаз. Ты глядя, на хороший написанный код на С, понимаешь что он состоит примерно из 90% обработки потенциальных ошибок. А сама логики размазана. Ты пишешь программу на Go, проверяешь в ней все ошибки исходя из слайда вручную. Каждая ошибка явно эксплицитно проверяется и дальше пробрасывается. И при этом ты говоришь про чистоту и простоту написания кода. Когда мы говорим, про то, что мы пишем код на Python, в котором мало обработки ошибок, то мы соберём stackstrace в Sentry, посмотрим всё что было, отберем то, что критичное и с этим будем разбираться. При этом код остаётся гораздо меньше. Чем меньше код, то в нем явно ненужной специфичной обработки ошибок, тем он будет более читаем. И как это совмещается?


    Ответ: Это хороший вопрос. На самом деле это моя личная боль. Это больно, то есть эта ситуация сейчас решается. Вообще, есть несколько паттернов, которые позволяют избежать обработки ошибок. То есть заменить обработку ошибок на паники, фактически аналогов Exception.


    Есть способы как это обработать. Например, сделать так, что функция в самом начале делают проверку в структуре.


    Вопрос: Попытаюсь как-то подытожить, это сводится к сноровке по обращению с языком?


    Ответ: Просто приходит человек. Он видит как это написано и код по аналогии. Если код изначально был написан нормально, то, он пишет нормально.


    P.S. Телеграм чаты:


    golang — Новости по языку программирования Go, ссылки на статьи и интересные проекты


    Golang RU — Чат про Golang


    Golang Jobs and Freelance — Jobs and freelance for golang developers

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 35

      +1
      Тапки можно кидать в меня (как автора доклада).
        +11

        Ну, раз вы предлагаете:


        С бенчмарков вообще принято сравнивать.

        Принято не значит правильно. Бенчмарки это сферические кони в вакууме, которыми приятно мерятся, но которые очень плохо соотносятся с реальностью. Сколько пишу код, очень часто код упиратся в базу и в том же https://www.techempower.com если глянуть на single/multiply то пора переходить на java/rust. Но даже в этом случае это синтетика, если у вас в коде запрос к БД выполняется 200мс, то влияние от кода и платформы становится довольно незначительным.


        Но мы можем попробовать использовать multiprocessing в Python.

        А почему не multiprocessing.shared_memory? Даже в доке по python написано, что Manager медленее


        Беда в том, что не все функции не блокирующие.

        Поправьте меня, если я не прав, но если в том же go взять не pure-go либу то она тоже будет блокирующей, и заблокирует один из воркеров, разве нет?


        Больше того, Select в Go еще крут тем, что мы хотим читать сразу из 5 разных потоков.

        "Когда у вас в руках молоток, все кажется гвоздями". Но всегда можно взять aiochan.


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

        Учитывая, что все эти либы написаны на C или C++, а python выступает просто удобным интерфейсом, потому что его синтаксис несколько проще для всяких казуалов? Не уверен, что будет существенный профит в производительности.


        Берём библиотеки Python и выясняем, что мажорная версия ломает обратную совместимость, причем ломает так, что это чинить неоправданно дорого.

        Потому что ввели два новых ключевых слова. Изменение имен переменных так неоправданного дорого, что кошмар.


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

        Думаю, в 2100 Go дойдет до общего регистра пакетов с корпоративным зеркалами, где помимо контрольных сумм еще будет запрещена возможность залить версию пакета повторно.


        Кодировки. Это забавная штука, в Go сразу UTF-8, а в Python с 3.7 тоже сразу UTF-8, но на практике не все утилиты/библиотеки об этом догадываются.

        Эм… с 3.0 же, или вы о чем?


        От блока про туллинг меня, конечно, довольно серьезно бобмит :) Как будто, если программистам не совать в лицо тем, что в языке есть профилирование, они его не будут использовать. Может не в программистах дело?


        Ну и вообще, python в основном предлагает вам выбор, хотите, можно построить себе обработку ошибок через монаду Result и общение между корутинами через aiochan и оно все будет довольно просто работать.


        Модель же go предлагает вам рабочую лошадку, из которой как только вы начинаете вырастать, язык приходится выкидывать и искать более гибкий язык. А там где "интересные задачи и highload" я так понимаю, из go вырастаю довольно часто :(

          –1
          Думаю, в 2100 Go дойдет до общего регистра пакетов с корпоративным зеркалами

          Уже есть https://proxy.golang.org/, а для зеркал https://github.com/gomods/athens. Добро пожаловать в 2100!

            0

            Но ведь передеплоить пакет в прокси все еще можно вроде, разве нет? То есть это, скорее всего, вызовет просто бугурт, так как чексуммы есть, но осадочек останется.

              0

              Не получится, там всё очень жестко.
              Т.е. если вы задеплоили пакет v1.0.1 (по сути сделав git tag v1.0.1), то изменить или удалить его невозможно. Да и деплой происходит не явно, никто в прокси ничего не пушит, просто тегают новую версию в системе контроля версий и всё автоматически подтягивается при первой загрузке.


              Есть sum.golang.org, можно почитать описание, если кратко, это проверяемый прозрачный лог хешей для модулей.

                +1

                Ну, я же могу в github удалить тег и тегнуть другой коммит? Или прокси навсегда кеширует у себя значение? В таком случае, там хотя бы можно сделать yanked релиз (удалить пакет из прокси)?

                  0
                  Или прокси навсегда кеширует у себя значение?

                  Прокси сохраняет значение в sum.golang.org, который по сути дерево Меркла, и сейчас там изменить хеш версии невозможно. Зато можно криптографически проверить все изменения версий :) Кстати, это всё работает по умолчанию в гошном тулинге начиная с версии 1.13, дополнительно устанавливать ничего не нужно.


                  удалить пакет из прокси

                  В общем случае невозможно, только если вручную мейнтейнером прокси в очень исключительных случаях.


                  UPD: забыл сказать, сам пакет прокси тоже хранит вечно, т.е. если даже удалить репо из гитхаба, пакет останется.

                    +6

                    Ну тогда добро пожаловать в 2100 :) Осталось только порадоваться.

            0
            >А почему не multiprocessing.shared_memory?
            Речь о хранении большого развесистого объекта. Поэтому Manager.

            >если в том же go взять не pure-go либу то она тоже будет блокирующей
            Нет. Вернее, она блокирует тред, но не блокирует работу ≤GOMAXPROCS активных горутин. Отдельно, в Go не-pure либы куда реже встречаются, и общий тренд на переписывание C-либ на pure Go.

            >Но всегда можно взять aiochan.
            В программировании можно всё. Но не всё удобно. Как я понимаю, с aiochan надо колдовать вокруг merge, чтобы различать разные источники сообщений.

            >вы о чем?
            Часть библиотек и тулов не готовы к UTF-8 исходному коду.

            >как только вы начинаете вырастать, язык приходится выкидывать и искать более гибкий язык
            >«интересные задачи и highload»… из go вырастаю довольно часто
            Это очень странное утверждение. Практика отказа от Go есть, но все мне известные кейсы либо про недостаточную производительность, либо про неумение архитектуры («текут горутины»), либо про смену менеджмента. К гибкости языка это отношения не имеет.
              +4
              В программировании можно всё. Но не всё удобно. Как я понимаю, с aiochan надо колдовать вокруг merge, чтобы различать разные источники сообщений.

              Нет, там есть select.


              Часть библиотек и тулов не готовы к UTF-8 исходному коду.

              Часть старых тулзов, которые еще работают на исключительно python2? Или вы про какие-то другие тулзы?


              К гибкости языка это отношения не имеет.

              Поправьте меня, если я не прав, но мне кажется, что на go довольно неудобно будет строить архитектуру отличную от горутины + CSP? Я это имел ввиду под гибкостью.

                0
                Речь о хранении большого развесистого объекта. Поэтому Manager.

                Хм… я же правильно понимаю, что это ваш бенч? Если да, то можно исходники?

              +4

              Ловите тапочки: автор доклада по фене ботает, вдобавок "расшифровщик" такого нарасшифровал, что сидишь и расшифровываешь что там автор доклада зашифровал и что там расшифровщик перезашифровал.
              В итоге очень тяжело читать.
              Начал читать с интересом (как питонист, приглядывающийся к Go), но примерно к трети очень устал и плюнул — глаза слезятся от такого текста.
              Очень тяжело.
              Не зашло.

            +1
            Привет, тут один знакомый постоянно устраивает холивары на тему, что python никуда не развивается и что у него написано кучу костылей, а go вообще придуман для послушных мартышек, которые не смогут сломать код. Что вы думаете на эту тему?
            Мне лично нравится и python и go, последний даже больше как то :)
              +1
              go вообще придуман для послушных мартышек, которые не смогут сломать код

              Практика показывает, что при желании могут. Но это, безусловно, сложнее, чем на Python :).

              +1

              Python, Node.js, Clojure языки для быстрой разработки(быстрой не значит некачественной).
              Равносильное по количеству бизнес логики приложение на Go или Java, писать долго и дорого. Соответственно если есть задачи требующие высокой производительности Go будет адекватным инструментом, но писать весь прикладной код на Go это мягко говоря странно, если вы не уровня Google где любой сервис это миллионы пользователей, трудозатраты перекроют любые плюсы по уменьшению потребления ресурсов железа.
              В целом на мой взгляд Go подойдет для ПО промежуточного — между прикладным и системным, где не так много бизнес логики, но уже важна скорость работы, скорость запуска и т.д.

                +3
                Равносильное по количеству бизнес логики приложение на Go или Java, писать долго и дорого.

                По моему скромному опыту, начиная с не самого большого проекта (над которым работает одновременно 10-20 человек уже достаточно) поддержка кода на динамических становится чуть ли не сложнее, чем на языках вроде Go (у меня больше всего опыта с PHP и Go, но судя по тому, что происходит с Ruby/Python/etc, там ситуация не лучше).


                Связано это в первую очередь с тем, что зачастую очень сложно/невозможно отследить все использования каких-либо функций или методов, или же это требует скрупулезной работы по проставлению phpdoc-аннотаций, которое по сути делает из PHP типизированный язык, только с не самым удобным синтаксисом и без строгой проверки типов.


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


                Единственное, что меня до сих пор смущает в Go это то, что относительно высокая производительность также позволяет навертеть очень сложную архитектуру (там, где это не требуется), которая тоже будет работать с приемлемой скоростью, и при этом поддерживать её сильно сложнее, чем альтернативное решение на том же PHP, потому что на PHP такая архитектура никогда бы не «взлетела» из-за того, что PHP относительно медленный. К сожалению, это тоже встречается в реальности достаточно часто, в основном когда люди приходят с других языков (гхм, Java например, ...)

                  0
                  >относительно высокая производительность также позволяет навертеть очень сложную архитектуру (там, где это не требуется)… поддерживать её сильно сложнее

                  Сможешь выделить кейс об этом? У меня все кейсы переусложённой архитектуры диктовались требованиями к производительности и стабильности во взаимодействии с внешним миром, т.е. совсем не про язык.
                    0
                    на Go многие алгоритмы не требуется оптимизировать, чтобы получить приемлемую производительность


                    В общем случае это утверждение должно быть ложным, всё-таки временная сложность не зависит от языка. Полагаю, в вашем случае та самая константа сыграла решающую роль.
                      0
                      поддержка кода на динамических становится чуть ли не сложнее, чем на языках вроде Go
                      Только в Go простейшая типизация, а в некоторых динамических даже дженерики возможны.
                      и без строгой проверки типов
                      с помощью отдельных инструментов, но тем не менее, это уже стандартный подход в Python/PHP и в своём роде (Type)Javascript и давно (по моему опыту на бэкенде по крайней мере).
                      Впрочем, я не про дженерики, а про то, что сравнивать по этому параметру мало смысла. Большие кодовые базы нуждались в контрактах и они их получили.
                      0
                      >Равносильное по количеству бизнес логики приложение на Go или Java, писать долго и дорого.

                      По моему опыту, для веб сервисов особой разницы нет. Если не нужно подтягивать монстров типа JMS, код пишется сравнимое время, отлаживается нередко быстрее альтернатив. Другой вопрос, что обычно при замене условной Java на Go в компании есть огромная экспертиза в Java, и очень слабая в Go — что вызывает проблемы с качеством архитектуры и скоростью разработки.
                      +2

                      Сравнение в духе «кто круче, слон или кит».


                      Лучше бы Go с Crystal сравнили. Это было бы куда как интереснее.

                      • НЛО прилетело и опубликовало эту надпись здесь
                          +3
                          Сравнение ну такое себе. Никто не спорит что Go быстрее питона, вот только мериться миллисекундами — это очень узкая ниша. В большинстве случаев ребятам из бизнеса важно, чтобы фичи делались в срок, а ребятам из разработки — чтобы код было легко читать и поддерживать. Для этого в питоне всё есть. Вот взять например Джанго — хороший фреймворк, есть все из коробки, полно батареек, миллион готовых решений. Где это все в Go? Да, asyncio наверное уступает в скорости Go, вот только если у тебя вся бизнес-логика на Python, то тащить новый язык ради одной-двух задач смысла нет. Я не спорю, что есть ниша — где выбор Go однозначен. Вот только это не значит, что это серебряная пуля он всех проблем.
                            0
                            Этот доклад был сделан ради слайда с зарплатами — как единственный ультимативный аргумент в выборе языка. Производительность при желании улучшается до необходимых значений, удобство работы, как показывает кодовая база enterprise проектов, вторична. Жаль, что из расшифровки это не так понятно, как из живого доклада.
                              0

                              На самом деле тоже сомнительный аргумент, просто язык моложе и специалистов по нему меньше, все популярные языки проходят эту стадию.

                            +5

                            Когда начинал переходить на Go, тоже был в восторге, какой простой, понятный язык и для всего есть готовые библиотеки. Но потом оказывается, что за простоту приходится платить и в нем нет многих вещей к которым привык в других языках. Библиотеки на все случаи жизни оказываются не доделаны и многое ещё не реализовано. В итоге пришось написать dll на другом языке, чтобы обойти узкие места в Go. В общем разочарован, возможно нужно глубже копать, но хочется писать код, а не искать как же всё-таки заставить его работать

                              0
                              Это очень редкий кейс, когда в Go не хватает именно производительности, и её не дотюнить за короткий срок. Расскажите о нём — это ценно.
                              +3

                              Кубики vs Пластилин — надо сравнить!

                                –2

                                А Go в этой метафоре кубики или пластилин?

                                0
                                Сравнивая зарплаты, стоит сравнивать и количество вакансий.
                                image
                                image
                                Причём в результаты поиска на втором скрине попали и вакансии, не имеющие отношения к Go, у которых просто сочетание букв «go» встречается в названии или ещё где-нибудь. Так что разница ещё больше. Грубо говоря, зарплаты немного больше, но получать их намного сложнее.
                                  0
                                  Всё так — Go-вакансий в разы меньше Python-вакансий. Но разработчики существуют в условиях огромного дефицита middle и senior специалистов, и поиск работы для не-джуна не представляет существенной трудности.
                                  Кроме того, Go вакансии в общем случае интереснее ввиду больших нагрузок и обрабатываемых данных, где производительность и удобной параллельной обработки становятся актуальными.
                                  0
                                  Очень хорошо, такие выступления подталкиваю хипстеров к низкому старту. Чем меньше их будет в питоне, тем лучше.
                                    0
                                    Года 3 уж изучаю Python (после многих лет изучения всяких других языков). Как увидел на нём код, сразу влюбился в синтаксис — всё просто, понятно и логично. И вот уже где-то год (или больше) хочу наконец-то изучитьт Go, который везде хвалят, гляжу на его синтаксис… и он мне просто не нравится, я его не понимаю. Чертовщина какая-то. Возможно, надо преодолеть некий барьер и дальше легче пойдёт, прямо не знаю. :)
                                      0

                                      Был в аналогичной ситуации. Первый месяц go вызывает недоумение и ненависть. Но где-то через полгода становится понятно что в этом есть смысл. А через год уже видится в этом гениальность. При этом go и python как бы перпендикулярны друг другу, для одних задач удобнее писать python для других на go. И хорошо если знаете оба.

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

                                    Самое читаемое