Search
Write a publication
Pull to refresh
18
0
Виктор Стародуб @vstarodub

User

Send message

Nginx у нас просто как балансировщик на входе, без какой-либо особо сложной логики обработки. Тут хочется что-то относительно быстрое, не прожорливое в плане ресурсов и хорошо знакомое devops-ам. По этим критериям выбрали nginx. Апач, наверное, тяжело уже в 2024 году назвать стандартным решением, особенно для балансировки.

Async/await выглядит похожим механизмом. Но без поддержки со стороны языка (прологи функций в go), например, нельзя сделать «автоматическое» вытеснение. Не то, чтобы без этого никак, но это удобно.

Горутины прозрачно запускаются в нескольких системных тредах, в этом принципиальное отличие от стандартных имплементаций корутин/fiber-ов.
Еще, по поводу mmap/unmap и соответствующих context switch. Цифр не нашел, специально померил — 150нс уходит просто на то, чтобы вернуть EINVAL. То есть это просто оверхед на сам факт вызова. А таких вызова будет 2 (mmap + munmap).

В Linux, адресное пространство у процесса меняется под несколькими локами (page_table_lock, mmap_sem, https://www.kernel.org/doc/gorman/html/understand/understand007.html) и такие операции будут выстраиваться в очередь. Плюс, там еще сама логика какое-то время будет занимать.

В простом бенчмарке запущенном с GOMAXPROCS=1 у меня получился оверхед на запуск пустой горутины в ~230нс (при этом все время проведено в user space):

package main

import (
    "runtime"
)

func main() {
    for i := 0; i < 1e7; i++ {
        go func() {}()
        if i%20 == 0 {
            runtime.Gosched()
        }
    }
}
Да, мне кажется, что основное преимущество тут в отсутствии переключений в режим ядра. Может быть я что-то еще из вида упускаю.

Получается, что для запуска горутины нужно сделать аллокацию на куче в 2Кб, поменять значение регистров и завести нужные структуры в шедулере. Все это быстро, т.к. почти всегда в user-space. Эти 2Кб целиком даже не обязательно трогать, если горутине будет достаточно всего, скажем, байт 100 стека. Отсюда и легковесность.

ARM-ов 32-битных по-моему еще достаточно много в телефонах. Например как минимум половина андроидов еще на версии ниже 5 (в 5-й начали поддерживать 64 бита). И, если я правильно понимаю, go достаточно активно пытаются внедрить на мобильных платформах.
Вообще говоря, malloc 2Kb и 24Mb будет выделять разными механизмами — 2Kb через brk (то есть, если место в куче есть, syscall-ов не делаем), 24Mb — через mmap.
Если выделить честные реальные 24Мб на горутину, то это будет ресурсоемко по памяти. Если выделять виртуальную и обрабатывать page fault-ы, то это будет ресурсоемко по CPU, т.к. не выходя из user space мы в принципе не сможем обработать этот случай. Более того, придется делать больше одного context switch-а.

Кроме того, нужно будет уметь отдавать обратно память в систему, что тоже проблематично из user space. Если использовать аллокации из кучи и копирование, то это в типичном случае должно быть быстрее, чем переключение контекста.

Ну и пока еще не умерли 32-битные архитектуры.
Я на самом деле хотел подчеркнуть, что не надо пугаться и в шедулер мы, строго говоря, не попадаем при каждом вызове функции :) Просто проверяем флажок.
Практически любой нетривиальной. Go много чего не инлайнит. Но планировщик мы должны вызывать только если действительно нужно вытеснить горутину. Более того, насколько я понимаю, там оверхед всего в одну проверку через которую сделано и увеличение стека, и вытеснение горутины.

Кроме того, вызов runtime-планировщика не такая уж и дорогая операция т.к. в отличие от планировщика ОС нет переключения контекста userspace -> kernel space. Это по сути вызов функции.

Но вообще, на каждый байт большого буффера/на каждый символ строки вызывать по несколько функций может быть довольно дорого если код критичный по производительности.
Отличная статья, только примеры чуть-чуть неудачные имхо: как раз пример с фотографиями/кадрами и сборщиком мусора мне кажется притянутым за уши. Я не совсем в теме мобильных приложений и графики, но подозреваю, что куча больших буферов одинакового размера будет примерно одинаково хорошо/плохо в плане производительности и оверхеда работать как с malloc/free, так и со сборщиком мусора. Malloc/free или, что примерно то же самое, ARC тоже не идеальны и возможно даже чуть более подвержены фрагментации, с которой надо отдельно бороться.

Равно как и алгоритмы обработки картинок (например, автоматическая яркость/контраст или резкость для фотографии), как мне кажется, как раз можно дооптимизировать до сравнимого с нативной производительностью (если не рассматривать рукописный код на ассемблере с SIMD), например с помощью упомянутого asm.js. Там ведь в идеале даже от оверхеда «виртуальных функций» можно избавиться, правильно пиля код на функции.

Но оба тезиса сразу теряют силу как только мы начинаем говорить о других видах приложений, не занимающихся хранением и обработкой больших монолитных кусков данных.
Тут дело про гит сказали. Иногда нужен именно SVN, но для типичного небольшого проекта чаще лучше гит. И с друзьями поделиться, и на github потом переехать будет удобнее. Отчасти из-за подхода «да я щас тут быстренько» мы и мучаемся до сих пор со всякими cvs/svn.

А если по существу было бы круче в 1-й команде показывать ту часть вывода 2-й (1-е строки commit message-ей), которая влезет в строку.
Так грабли там положили гораздо раньше, когда классов лишних придумали там, где они не нужны. Наступили, да, потом :) Если б статья называлась «Грабли 2: лишние сущности в коде», то вопросов бы не было.

Тут не то, чтобы дело в том, что надо отказываться от развесистой диаграммы классов, тк она в С++ (Java, ...) плохо ложится. А скорее то, что развесистая диаграмма классов — это слишком сложная модель в принципе, в ней много лишнего, как ее не реализуй.
Ну по идее да, там должен быть код ядра, но его, чисто теоретически, можно и в нижние 3Гб упихать, там ничего секретного нет. Что важнее, там всякие таблички для процесса: маппинг дескрипторов файловых и прочей ерунды. На винде вроде бы из за их потенциальных размеров они даже не сразу их в 1Гб научились упихивать.
По-моему, тут просто оверинжиниринг и грабли именно в этом, а не в виртуальном наследовании. С++, конечно, дает себе в ногу выстрелить в этом плане, но зачем это делать? Непонятно, зачем вообще усложнять модель и разделять Renderable и Updatable. Во-первых, кода на этом мы ненаэкономим. Во-вторых, скорее всего, и в читаемости ничего не выиграем, бедным программистам контролов просто надо будет писать больше кода.

Тут вполне можно обойтись одним классом типа Widget и от него наследоваться. «Фасад» он будет или просто тупой как валенок, это уже другой вопрос. По идее, если грамотно итерфейсы использовать, тогда двойное наследование не должно быть нужно вообще. В случае когда один контрол явлется одновременно двумя другими ИМХО лучше использовать составные контролы-контейнеры.

Еще (судя по тому что я видел/писал) чаще всего виртуально наследуются от единственного класса на весь API, тогда меньше путаницы. Но даже этого, как оказывалось, проще избегать, чем мучаться потом.
Ну я верю, что там побольше измерений будет на самом деле. Но не суть, просто обычно чем выше по иерархии, тем больше отчитываться и формальностей вообще, это не связано с full-stack-овостью. Например, если техлид — это почти архитектор, значит, вероятно, ему нужно как минимум с кучей народу согласовывать то, что он напридумывал.
Проблема тут не совсем во мне, GoF и еще что-то, уже не вспомню, что именно, я читал на эту тему. Правда, названия тоже уже выветрились частично. Но и до прочтения я почти все это видел в коде так или иначе, наверное поэтому GoF для меня откровением и не стал.

Проблема в том, что общаться приходилось с разными людьми, а многие, как и я, забыли названия, или предпочитали читать другие книги по программированию. Да и обсуждать код с точностью до проектирования классов приходилось только со студентами, а там обычно уже не до паттернов, там бы просто «хорошие привычки» привить бы сначала. К моменту того, как они понимают архитектуру проекта и приобретают эти «хорошие привычки», они тоже многие паттерны уже в коде успевают по 100 раз увидеть.

Часто надо было обсуждать код с точностью до символа: когда мы сочинали либы и хотелось, чтобы лишнего мусора программистам писать не приходилось. Но тут уже не паттерны важнее были, а удобство, лаконичность и интуитивность.

(Если что, то я в основном на C/C++ писал, немного JS. Для Java тема, наверное, может быть чуть-чуть более актуальной)
Мое наблюдение: терпимость к такому switch-еванию еще очень сильно зависит от IDE/редактора. Но мешанина из табов и пробелов в 6-м(!) уровне вложенности должна намекать на то, что и автору кода написание все-таки дается не совсем легко.

Но имхо, опенсорсный код должен все-таки более-менее нейтральный код-стайл выбирать, если он, конечно, не для галочки опенсорсный. Лично у меня проблема была бы в чтении не столько в количестве кейсов, а в том, что там сильно разнородный код: там и макросы пишут, и пути склеивают и чисто UI-код. Собственно, там помимо вышеописанных еще как минимум абстракций бизнес-логики (макрос, открытый документ, типа того) не хватает, чтобы этот switch их только «склеивал воедино»
Я не минусовал, но могу предположить, что про «пытался сэкономить» это был сарказм для усиления эффекта, а не попытка понять автора. Суть — в последнем предложении.
Мне кажется, что такие фразы все-таки опасны. Тут на, мой взгляд, и о проблеме и о конкретном решении (даже синглтон тот же можно слегка по-разному сделать, не говоря о трудностях перевода и разного background-а) приходится догадываться и есть риск ошибиться. У меня лично как-то обычно не было нужды в паттернах, как «промежуточном» слое терминологии между идеей и кодом. Быстрее и нагляднее получалось примеры кода накидать в 1-2 строки и сравнить подходы.

Один мой бывший коллега так вообще только паттернами и разговаривал (часто, похоже, и мыслил тоже) и в переписке при просьбе руководителя расшифровать какой-то из не самых распространенных в С++ паттернов, дал ссылку на книгу на амазоне (не на википедию или еще какой-нить доступный текст). Причем, похоже, абсолютно без задней мысли.
Ну тут же еще нужна любовь (желательно вместе с умением) к управлению. Один мой товарищ, когда вынужденно стал тимлидом, взвыл от необходимости гораздо более формально планировать и отчитываться о своей работе. Ну и куча народу есть, которая митинги всякие не любит, а тут уже не отмажешься.
Где-то крутой совет читал: разделять в резюме навыки на активные (например «пользуюсь раз в неделю») и пассивные («раз в месяц» или «писал много, но с того момента прошло 2 года»).

Ну и естественно, под свой список можно и не 2, а 3 категории подобных сделать. А какой-нибудь нерелевантый опыт работы, мне кажется, может оказаться проще и опустить (например халтурки во время учебы с участивем зоопарка технологий).

Еще совет читал из той же серии: писать только важное и главное, понимая, что резюме должно заинтересовать за первые 15 секунд прочтения. Я конечно, сам не суперспец по написанию резюме и не всех кадровиков готов оправдать (один раз мне дали лист 3-4-буквенных аббревиатур и спросили что из этого я «узнаю»). Но что-то в этом «умении себя подать» правильное и здравое есть.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity