Задача из Ozon: Golang собеседование
HTML-код
- Опубликовано: 20 сен 2024
- Мой курс по разработке микросервисов: clck.ru/389FPG
Мой Boosty: boosty.to/olez...
Linkedin: / olezhek28
Telegram-канал: t.me/+B_uc42KD...
В этом ролике мы разберем популярную задачу с golang собеседований. Golang нынче стал хайповым языком программирования и многие люди переходят на него с других языков. Поэтому и важно научится решать эту golang задачку, чтобы не ударить в грязь лицом на собесе для golang разработчика. Можно сказать - этот ролик небольшой урок по golang, потому что для понимания некоторых аспектов этой задачи нам нужно заглянуть в go поглубже. На практике golang порой бывает неочевидным и важно понимать, что за этим стоит. И ещё, в конце ролика я накинул пару вопросов на подумать для вас. Смотрите до конца и пишите свои ответы в комментах.
Олег вырост, когда-то смотрел собеседование на middle разработка, а уже ведущий разраб и сам уже контент пилит)
Как говорится: «все течет, все изменяется»
Ждём курсы )))
@@olezhek28go если у меня не было опыта в других языках. что бы вы советовали для бекенда учить нод джс или все же го?
Очень понравился стиль изложения, и задачка интересная. Хотелось бы видеть еще подобные видео с Гошными премудростями )
Спасибо:))) думаю еще будет
Скоро мое первое техническое собеседование на разработчика go ,недавно решал эту задачу ,решил ,но после вашего объяснения подчеркнул много нового для себя и все стало гораздо понятнее ! Посмотрел на одном дыхании в 5 утра и вместо усталости или сонливости только больше загорелся всем этим ,спасибо !
Как здорово!) Удачи на собесвх)
for range возвращает index, value. Если запрашиваем одну переменную, то индекс. Поэтому 0,1,2,3,4
верно)))
Покажи конечный продукт где такое применяется с горктинами и каналами !! А то весь Ютуб показывает код , а где это работает и какую пользу несёт не ясно . Чтобы понимать зачем вообще такое спрашивают
@@aleksanderpeshkin2266 без разницы от того понимаем мы или нет зачем такое спрашивают - спрашивать все равно будут) и если хочешь устроиться - знать должен
@@aleksanderpeshkin2266 многие вещи, которые спрашивают на собесах, нужно знать только для того, чтобы отвечать на вопросы на собесах
очень понравился стиль изложения, поэтому было бы круто увидеть больше видеo про подобные тонкости🙂
Спасибо:) буду стараться делать новые видео
Спасибо, очень полезный разбор, лайк подписка телега все дела. По ходу просмотра ставил на паузу сразу после вопроса и отвечал, потом сверялся с правильным ответом.
Хотел бы добавить, что в Go 1.22 изначальное написание будет именно 0,1,2,3,4 (в рандомном порядке) как это и ожидается.)
Все так, я это в ролике в какой-то момент упомянул:)
Олег объясняет как боженька. Так чётко и просто всё разложил, спасибо!
Спасибо, рад что понравилось:)
Очень классный формат, хочется больше таких примеров) Прикольно что сначала можно было попробовать самому решить задачу, а потом увидеть где не прав)
Спасибо) думаю буду периодически делать такие ролики
13:10 второй вариант каунтер прописанный снаружи рутин увеличивать внутри рутин. Либо передавать его по ссылке, либо создать в области видимости рутины. Но тогда надо мутексами блокировать запись другими рутинами, а потом вывести и разблокировать. В этом случае выведет по порядку, и это самый хороший исход.
Требуем больше познавательных роликов!
Если получится, то в эти выходные буду снимать :)
13 минут мучали передачу счетчика в аргумент 🤣 восторг
А потом такой - ну это починили уже за нас:)
@@olezhek28go слишком много воды, бро
Супер!! Слов нет!!
Рад, что понравилось))
6:40 значит wg.Done надо делать с defer в самом начале рутины
Такое поведение (4, 0, 1, 2, 3) повторяется. У меня предположение, что горутины с 0 по 3 ставятся в локальную очередь. А горутина с 4-кой ставится в LIFO часть глобальной очереди и оттуда и исполняется первой (потому что она уже есть в кэше процессора), а уже за ней те, что локальной очереди.
Полагаю так и есть:) кстати, случаем нет статейки про это под рукой? а то я этот дивный факт знаю, но вот пытался статейку найти, чтоб кидать людям и обычно просто про очередь упрощенно пишут
@@olezhek28go постарался поискать где я эту информацию взял, но не нашел сходу
Размер локальной очереди 256. А так гуглить runnext, это поле у P. Новые горутины получают приоритет, при этом используют остаток временного слота горутины-создателя. Каждый go stmt забрасывает новую горутину в runnext и шедулер возьмет сначала горутину из runnext и только потом начнет разгребать локальную очередь.
На русском я не находил ни одной толковой статьи. Есть на английском. Можно почитать с браузерным переводчиком. Только тутуб комментарии с ссылками не пропускает.
@johnsc4521 странно, что ссылки не пропускает, вроде должен
Thanks for detailed explanation
Did you read subs?) Is it helpful?
I understand Russian, I just have difficulties with writing, so I did not use subs. @@olezhek28go
Interesting:) Where are you from?)
0:40 ничего не выведет. Функция main закончится до начала вывода в го рутинах, может быть даже раньше запуска первой из них.
по поводу первого вопроса
все будет работать абсолютно также как и на нескольких ядрах, никакой разницы нет с точки зрения планировщика. 4 выводится первой потому что у нее по сути кеш прогрет больше всего. там вроде как есть стек размером в один как раз для этого, чтобы брать оттуда задачи первыми. Правильно?)
Ага, все так)
От 0 до 4 потому что i это индекс элемента в слайсе.
4,0,1,2,3 потому что 4 как ты говорил в других видосах кладется сначала lifo очередь, т.к она ближе всего к контексту горутины из которой запускается.
Все верно))
Спасибо, и формат супер и было полезно. Пожалуйста, не останавливайся!)
Меня на собесе ещё спрашивали как оставляя горутины сделать, чтобы числа выводились последовательно и желательно было набросать и объяснить пару вариантов
Спасибо:) и какие варики в голову пришли?
@@olezhek28go через каналы или wg.Wait() поставить в конец цикла
@@olezhek28go мне кажется тут можно канал использовать и читать из него числа
Формат отличный. Покачали задачу. Так намного интереснее
Спасибо, рад что зашло:)
респект за инфу, про то что в замыкания по прокидываются переменные, не знал, прокидывал всегда все через аргументы функции
Круто, что узнал что-то новое:))
Спасибо, позновательно!
17:40 Можно предположить что планировкщик задач GO в любом случае использует какой то алгоритм для выбора порядка выполненния задач в отведенное ему процессорное время. И он не просто выполняет их по порядку, а например пытается "усреднить" нагрузку дергая из разных частей списка задач.
15:59 Если правильно понимаю первая переменная при исользовании range - индекс записи. ( индек, значение := range слайс)
А вот почему такой вывод в 22 версии непонимаю. При планировании горутины делается снимок используемых переменных?
Если выводить адрес переменной в 21 и 22 версии, наблюдается разное поведение. В 21 он в каждой горутине одинаковый. В 22 - каждый раз новый адрес. Причем как если передавать его как аргумент и если через замыкание. Не наткнулся в патч ноуте с ходу на это. Есть какой то док описывающий поведение?
Загугли "Fixing For Loops in Go 1.22", ещё там внизу статьи есть раздел More Information с ссылками на дизайн-док и фак.
А если вкратце и на пальцах - раньше переменная цикла имела областью видимости весь цикл, а теперь областью видимости будет только итерация цикла.
не, там прикол том, что в шедулере у каждой М есть очередь фифо и одноэлементный лифо, мы в него кладем последний элемент, но и брать будем сначала из него
1)горутины не параллельны, а конкуренты, то бишь рантайм под коробкой решает как переключаться между ними, пока одна горутина спит другая работает
2)мы берём индексы слайса, п не сами значения, поэтому такое и поведение
1) ну врядли на одном принте будет какое-то переключение между горутинами
Я правильно понял, что ответ на первую задачу примерно такой: го выбрал последнюю горутину из-за кеша, а потом выполнил остальные в последовательном порядке?
Ага, типо того:)
Чтоб в выводе было 1-5, надо так: for _, i := range
Верно!:)
@@olezhek28go Спасибо за задачки!:) Кстати, я решил оставить в Print() вывод адреса WaitGroup. Результат меня удивил 0_o Адреса либо были разными все, либо частично были совпадения, либо все одинаковые. Вопрос: Потому ли это, что WaitGroup на стек попадает?:)
Что-то не совсем понял, то есть вывод ключей или индексов слайса, дает последовательность?
@@Erdaulet100 Привет)
У слайса нет ключей (ключи есть у map). Слайс - это абстракция над массивом. У массивов и слайсов есть элементы, а у элементов есть индексы. В Go существует 2 способа пройтись по слайсу:
1) использовать обычный цикл for:
for i := 0; i < len(things); i++ {
things[i] =
}
2) использовать цикл for range (что работает как с последовательностью в Python, но в Go это всегда либо слайс, либо массив):
for i, v := range things {
// i - это копия индекса текущей итерации 0...len(things)-1
// v - это копия значения элемента things по индексу i (как будто v = things[i])
}
Записывать в копии i и v нет смысла в цикле for range. Они используются для чтения из них. Если нужно записать в элемент слайса в этом цикле, то, как обычно, используется запись things[i] =
7:40 нет, не всегда, в горутине может что то вечнозациклиться. Тогда вроде даже паники не будет, просто будет висеть вечно. или рутина начнёт ждать чтение/запись канала тогда поидее может быть паника и дефер таки выполнится но это не точно, надо б проверить.
8:15, а спасибо, не знал. Теперь знаю.
Может быть ошибка с тем что i = 5 для всех выводов актуально до какой то версии go? У меня версия 1.23 и не воспроизводится ошибка о которой вы говорите, i передаётся в горутину будто копия, чуть пошаманив выглядит так, что на каждй итерации в горутине захватывается новый указатель на i и из-за этого и не воспроизводится ошибка, будто при изменении значения i - захватывается(в замыкании) новый указатель, что не производит того эффекта, что мы ожидали (вывод 5 из каждой горутины)
Сорян не доконца досмотрел)
Fixing For Loops in Go 1.22
Очень понравилось, спасибо
Здорово!:)
будут ли ролики по внутрянкам го? типа работа gc, аллокаторы памяти, планировщик горутин и тд
Все может быть, но пока точно ответить не могу)
при log.Fatal defer не вызовется, поэтому пишет log.Panic )
Как варик)
Спасибо за рассказ о фигче loopvar.
Наконец то нашел повод попробовать:)
@@olezhek28go Штука интересная, но она вызывает у меня настороженность. Уже представляю ситуации, когда девелопер пишет код и этой активированной функцией, а в билд пайплайне это причуда не активирована - и код уходит в продакшн.
С февраля это будет по дефолту в языке
Ответ на первый вопрос - параллелизма нет, но есть конкуренция. Ответ на второй вопрос: если для рэнджа указана одна переменная, то это индекс, а индексы с нуля идут
Стиль изложения - огонь! На Go не пишу, но почти ни с чем не ошибся. Могу только предположить, сколько тысяч человеко-лет гоферы потратили на дебаг таких ошибок))
Впрочем, этот наш простой пайтон - тоже нифига не простой)
Спасибо:))) на самом деле многие ошибки подобные в бест практисах учтены и некоторые люди не косячат в них и даже не понимают почему ахаха
Олег, я крутой гоандмастер бит!
Ееее!)))
загадки для джунов ))) кто ж так range пишет))
вопрос в догонку к GOMAXPROC(1). А какой порядок будет если мы перед wg.Wait() time.Sleep(1) добавим?)
А если потом перед time.Sleep(1) добавим runtime.Gosched() ?)
Исходя из моих прикидок, кажется что такой же, но может что-то упускаю) хотя…
Слип это системный вызов, стало быть впору переключить контекст, дальше идет выполнение горутин в том же порядке и даже если секунды не хватит, то маин горотина же по идее залетит в глобальную очередь и оттуда наврядли раньше успеет вычитаться чем остальные, 1 к 61 как никак) но это так мысли в слух) надо завтра поиграть
@@olezhek28go а вот тут и есть прикол ) Если делать слип без Gosched, то вывод будет 0 1 2 3 4, а если хоть раз до этого сделать Goshed, то уже 4 01 2 3 )
Если просто порассуждать т.к. истинная причина не ясна, то получается при Sleep последний стек будет не не наших горутин, а слипа, поэтому исполнение горутин будет FIFO, а Gosched в свою очередь переключит на последнюю вызванную горутину т.е. будет 4 0 1 2 3 )
Получается 0,1,... Наверное потому что schedule прежде чем жертву выбрать проверяет таймеры. Видимо sleep(1) проходит слишком быстро и schedule опять выбирает main горутину в checkTimers. Можно увеличивать время сна и в какой-то момент 4 из runnext запуститься, но если таймер успеет сработать, то main горутина через checkTimers может влезть вперед local queue, в любом месте. Если Gosched добавить, то текущая горутина уйдет в конец local queue, а scheduler возьмет 4 из runnext. В данном примере с глобальной очередью вообще ничего не должно происходить, ведь capacity локальной очереди 256 + timers heap у каждого P свой.
P.S. Sleep(1) это не 1 секунда, а 1 наносекунда. Duration в наносекундах. Там где duration не стоит указывать просто int, об этом 75 глава 100 Go Mistakes
Я видать вечером не заметил, что там секунда не стояла) забавно то, что с секундой все ведет себя также) я из этой предпосылки исходил
@@olezhek28go оу... слип 1*time.Second это перебор для смены контекста) просто time.Sleep(1) ) вот такая у нас уличная магия... в рот мне ноги.
Всё, теперь в озоне разрешили defer ?))
А запрещали?
Задача топ! Да, простая. Да, боянистая. Но даже на таких задачах можно завалиться.
Ага, иногда люди на ней такую базу выдают)
Привет. Мне предлодили оффер в озон. Можешь рассказать чтото про перфоманс ревью и индексацию зп? Думаю отказаться только изза этого. Начитался отзывов на дримджоб
Напиши мне в личку в тг - olezhek28
А можно мне такой же оффер
@@medall1st можно. Я отказался
Когда примерно будет старт курса по разработке микросервисов?
Второй поток сейчас заканчивается, а новый будет уже после НГ думаю
пока смотрел видео, постоянно переключался на телеграмм свой думал что мне пришло сообщение и никак не мог понять, wtf, потом как дошло)
Там реально слышно?) я че-то при монтаже не заметил(
На сколько известно в GO рутины легковесные (2к) и одном ядре может быть несколько рутин, поэтому параллелизм сохраняется даже с одним ядром, поэтому и вывод такой
Все так, но горутины то в очередь последовательно кладутся)
параллелизм невозможен на одном ядре))) ядро физически не может выполнять больше одной операции за один тик процессора
@@kidayano всё так, если мы гипертрединг не берем в расчёт)
Тот случай, когда основной синтаксис языка видишь быстрей, чем читаешь в учебнике🥴
Это хорошо?:)
азазза голову ломал, откуда 0 и нет 5
угар, всем бобер кувра!
Ахаха
интересно, автор видео согласовывал нейминг видео? не будет ли каких-либо юридических проблем, так как Олег ранее работал в Озон?
Это ж больше внимание привлечь, задачу то эту где угодно спросить могу , ибо уж очень популярная)
дейстивительно боянистая задача. на первом же собесе по го ее спросили)
Ага) тем и удивительнее, что еще не все научились ее решать
хех, а я думал алгосики спросят на го, а нет - собес как старые джава собесы, где квиз на квизе
Ахаха алгосики в другой секции)
нет тайной ложи, есть явная лажа, если задаваться вопросом назовите где может быть использован этот алгоритм на практике или в жизни, как часто этот алгоритм пользуется в проектах? а на собеседованиях обычно спрашивают то что сами в компаниях не пользуются и тогда появляется мысль может это не имеет значение от слова совсем
А вы это мнение со стороны высказываете или сами на гошке пишете?) на мой взгляд почти все аспекты языка, которые вскрывает эта задача, регулярно встречаются на практике.
@@olezhek28go побольше практических примеров для каких целей и я Вас благодарю и целую монитор с вашим кодом
Всё элементарно просто, 4 ядерный процессор. Поэтому и получается такая последовательность. Четвёртое ядро 1, 2,3,4
Специально не смотрю комментарии. Верный ответ?
А это на какой вопрос?) про ситуацию с го макс проц?
Последняя загадка настолько меня впечатлила, что я понял что не писать на голанге выбор правильный 🤨 даже JavaScript привели в чувство, по сравнению с этим вашим Го
Да на самом то деле это больше интересный момент, чем что-то важное на практике
Я ещё не понял loopvar. Что это?
это просто значение переменной окружения, которая позволяет врубить экспериментальный функционал языка) с февраля это будет в дефолтной версии языка
Это задача для миддла или джуна?
Да такое и на сеньора спросить могут, просто ответ разной глубины устроит и конечно эта задача будет не единственной:)
Олег, можно консультацию получить у тебя, по дису например, если да то где контакт спросить?
В телеге olezhek28
а откуда взялся 0?
С нуля же цикл)
@@olezhek28goа точно, я подумал i это значения массива
Братик это хайлвл, это только учу гошку мне трудно понять что происходит =/
Придет время и все поймешь;)
@@olezhek28go да это вообще фэйл =/ я знал питон, пришел в универ там были ++ и java, я пострадал может недели 2 и все ок стало. Java даже понравилась, начала ковырять Go, начитался комментариев “go самый простой язык ко ко” но пока мне он больше боли доставляет =/
Устарело) Теперь итератор per-iteration, а не per-loop
upd: а, блин, досмотрел)
А загадка про конкурентность vs параллельность.
Ахах)
Загадка про один тред то?
В js вообще всего один поток. И тем не менее конкурентности хватает)
1
поздравляю)
серьезно? ноги целовать за знание элементарной базы? что не так в этом мире? хотя чему тут удивляться безработному программисту с 20 летнем стажем
Ну камон, ирония же была:)