Pull to refresh
2
0
Alexei Kornienko@Alexei_987

User

Send message

Для некоторый языков (например Rust) это выглядит как довольно очевидная оптимизация на уровне самого языка. Там есть оператор ? который как раз пробрасывает вверх ошибочное значение и ничего не мешает на уровне реализации этого оператора правильно поставить likely/unlikely в нужную ветку. И даже если он не стоит то думаю предсказатель ветвлений и сам разберется и будет в большинстве случаев правильно определять ветку выполнения кода. Но основная моя идея это как раз то что 1-5% процентов скорости выполнения не стоят значительного усложнения процесса написания и поддержки кода и лучше использовать единый подход везде.

Основная проблема такого подхода как мне кажется это то что в итоге у вас в проекте будет использоваться 2 разных подхода к обработке ошибок:
1) В случае исключительной ситуации (потеря соединения с базой данных например) это будет выброс исключения
2) В случае например передачи в функцию невалидных параметров (что в теории бывает часто если ваш код обрабатывает некие данные снаружи) это будет возврат кода ошибки


В итоге получается что ваш код должен учитывать одновременно обе стратегии и реализовывать обработку обоих вариантов например для отображения пользователю корректного сообщения.
Мне лично кажется более удобным с точки зрения удобства программирования когда в программе есть ровно один подход по обработке ошибок и например в случае Python это обрабатывать все исключениями (все равно он не очень быстрый), а в случае например Rust это удобный механизм передачи ошибочных значений вверх по стеку. Думаю что современный компилятор достаточно умный чтобы самостоятельно поставить unlikely в код по обработке ошибочного значения и тогда замедления практически не будет.

Гореть работой можно пару выходных (ну если очень хочется дописать кусок алгоритма). Если кранчевать по полгода то любые идейные люди выгорят и отупеют… невозможно 60 часов в неделю писать высококачественный код… не знаю конечно как у вас, но из моего опыта чем ближе к релизу, тем более страшные костыли приходилось лепить в код чтобы оно хоть как то работало потому что требуют все прям на вчера… Опять же из моего опыта работы вне геймдева — вполне можно найти варианты и договориться с заказчиком работать в нормальном темпе (без переработок) и при этом делать нормальное качество — с автотестами, перфоманс тестированием и оптимизацией и прочими вещами которые в итоге не стыдно показать… А как раз тот код который я писал в геймдеве за рядом исключений, реально стыдно вспоминать…

Читать такой заголовок просто смешно… Работал пару лет в геймдеве и там вечно руководство выставляло абсолютно нереальные сроки… что опять же подтверждается тем что программисты работали по 6 дней в неделю (и наверняка еще по 10 часов в день) сам работал так в течении полугода и после этого решил завязать с геймдевом… Понятно что при таком режиме работы, и когда над душой стоит продюсер и торопит, нормальный проект не сделать… А теперь руководство милостиво решило не урезать премии и взять вину на себя… Скорее всего это сделано с единственной целью — удержать оригинальную команду разработки чтобы она успела пофиксить баги… т.к. иначе люди разбегутся и проект вообще навсегда останется с этими багами т.к. экспертиза утрачена..

У меня 3 раскладки, но обычно использую две (третью редко). Я себе настроил Капс включение англ. раскладки, Шифт+Капс включение русской. Очень удобно и решает проблему когда раскладок несколько. Остальные редко используемые можно выбрать мышкой если нужно.

А еще в Red Faction можно было ездить на шахтерском комбайне — https://youtu.be/FbnJ_OKvuQc?t=177 и накопать туннелей в непредсказуемые части карты. На те года вообще отлично было сделано

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

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

Не вижу сложностей если честно… что мешает создать 100-1000 горутин? вопрос только в том что каждая отдельная горутина должна работать достаточно долго для того чтобы можно было пренебречь расходами на ее создание. т.е. если у вас ~ 250 нс уходит на создание горутины значит она должна работать хотя бы несколько секунд и сделать много итераций. Думаю лучший вариант бенчмарка будет сделать простой счетчик и инкрементить его из множества горутин. это позволит симулировать плюс минус реальный сценарий и убедится что все работает корректно. Моя основная идея в том что реализация на каналах будет подвержена проблеме "Проснувшегося стада" (https://en.wikipedia.org/wiki/Thundering_herd_problem) и надо проверить работу этого сценария и подумать можно ли улучшить что-то в реализации для того чтобы его избежать

Если я буду блочить через Cond.Wait() то мне придётся разлочивать Cond на основании <-ctx.Done(), а этого я без дополнительного потока не смогу сделать.

А вот это уже печально, я надеялся что Го позволяет делать select на канал либо Cond… если это не возможно то это очень печально (и еще раз подтверждает мои мысли что Go это недоделанный язык)

1-й бенчмарк не имеет смысла впринципе т.к. проверяется по сути скорость работы атомик операций на вашем процессоре. единственное его предназначение это может быть поиск каких то простейших ошибок. В случае если горутин множество 2 горутины слишком мало для того чтобы собственно косяки Вашей реализации стали очевидны. Сравните именно случай где за один мьютекс конкурируют 100 потоков и сравните разницу для вашей реализации и для стандартного мьютекса

Бенчмарки кривые…
Я предполагал что будет что-то вида:


mx = Mutex()

for i := 0; i < 100; i++ {
    go func() {
        for j := 0; j < 1000; j++ {
            mx.RTryLock(ctx)
            sleep(0.001)
            mx.RUnlock()
        }
    }

}

Плюс к этому:
Основная проблема каналов это то что для обычного мьютекса (не RWMutext) если он освобождается то достаточно разбудить ровно один другой поток и дать ему залочить мьютекс. В случае же канала если у вас один мьютекс ожидает например 100 потоков то вы разбудите их все, а потом одному из них удасться захватить мьютекс. Это ужасно неэффективно и вот собственно этот момент надо сравнить в вашей реализации и в стандартной либе…
Реализацию через Cond не вижу проблем если честно… ровно в том же месте где у вас закрывается канал необходимо выставлять событие "мьютекс освободился" и все кому это интересно могут проснуться и попытаться его захватить

Возможно я что-то не так понял (У меня мало опыта программирования на Go), но у вас очень странные бенчмарки т.к. вы тестируете только быстрый сценарий (Фактически у вас только одна горутина пытается захватить мьютекс и поэтому всю магию каналов вы фактически не проверяете). Ну и кроме того внутри канала лежит самый обычный мьютекс просто он спрятан от вас… Думаю более правильным и эффективным будет использование Cond (https://golang.org/pkg/sync/#Cond) вместо канала. А вообще странно конечно что такие простые вещи не реализованы в стандартной библиотеке и приходится писать свои велосипеды

Вот так опять нельзя. Borrowchecker стал лучше и поэтому ваш пример компилируется т.к. фактического нарушения у вас нет (время жизни переменной y заканчивается сразу после *y += 1;) https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=c58504a8417accd7a57d65355ec6f23e

а ассемблер это в чистом виде синтаксический сахар над машинными кодами…
а Си это синтаксический сахар над ассемблером…
...


Странно почему вы с таким подходом не программируете напрямую в машинных кодах… зачем вам этот синтаксический сахар?

Это кстати в теории можно даже рассматривать как отдельный критерий для выбора языка — средство доставки вашего приложения до конечного пользователя. По сути для Go/Rust/Python существуют свои собственные менеджеры пакетов и у всех их есть свои недостатки. А если собирать например deb пакет то тут у Go/Rust будет преимущество.

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

Про пользу dependency injection. Я относился к нему пренебрежительно пока в 2014 на себе не почувствовал его магическую силу.

Я не отношусь пренебрежительно к DI подходу, как я уже написал есть отличные примеры его использования в питоне (pytest). Есть отличные примеры его использования в других языках (Java Spring). Но дзен питона в том что явное лучше неявного. Писать специальный фреймворк задача которого создавать обьекты по определенным правилам стоит только в том случае если этот фреймворк позволяет удобно решить проблемы которые возможны при создании обьектов:


1) Самостоятельно определять порядок создания обьектов. Ваш фреймворк этого не делает и требует явного описания порядка


2) Управлять временем жизни обьекта. Условно есть обьект представляющий собой транзакцию в БД время жизни которого — запрос, есть обьект соединениу к базе данных, который живет постоянно. В таком случае DI фреймворк управлял бы выделением соединений из пула и созданием временных обьектов и т.д. Ваш фреймворк насколько я понял такого тоже не умеет.


Мой фреймворк позволяет описать всю структуру приложения в декларативном стиле не создав ни одного объекта. В таком виде со структурой удобнее всего работать.
Писать все в функцию create_app() неправильно. Это смешивание декларативной и runtime частей. Такие приложения тяжелее понимать.

Кому удобнее? Вам как автору фреймворка или вчерашнему джуну который его первый раз видит? В чем профит описания структуры без создания объектов? Для того чтобы понять структуру приложения разработчик читает его код, в процессе чтения кода объекты не создаются. В процессе работы приложения обьекты все равно будут созданы и без разницы произойдет это в функции create_app или в контейнере.

Хороший пример DI в питоне — фикстуры в pytest. Там они действительно резолвят зависимости плюс позволяют гибко управлять временем жизни конкретных фикстур.
В данном конкретном случае:


1) все объекты создаются на этапе инициализации, т.е. это абсолютно тоже самое что руками написать код создания объектов.


2) все эти объекты в общем случае существуют все время выполнения программы и соответственно ApplicationContainer никак не управляет их временем жизни.


Соответственно вообще непонятно какую именно пользу приносит данный фреймворк и почему он громко называется Dependency Injector.
Я с таким же успехом могу назвать метод __init__ DI фреймворком

Information

Rating
Does not participate
Location
Харьков, Харьковская обл., Украина
Registered
Activity