Максим Овцин

Максим Овцин

Неделя
Oct 26, 2020 → Nov 1, 2020
Темы

Архив недели

Понедельник


Всем привет! Меня зовут Максим и я новый автор недели. Работаю в лондонском офисе Facebook в ДевИнфре над Swift adoption. 🙂

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

План будет примерно такой: Monday - A little bit about me. Tuesday - Singapore vs London. Wednesday - FAANG Interviews and FB experience. Thursday/Friday - Swift Compiler, Performance and You.

Monday: До FB я около 7 лет работал в клиентской разработке, успел поработать и в аутсорсе, и в стартапах, и в более менее зрелых компаниях. Но началось все относительно случайно. Живя в общаге ИТМО, от нечего делать, я решил установить на свой Асер Хакинтош...

Две недели без сна, минимум еды, много стресса, тонны прочитанных мануалов по бутлоадерам и прочим хакам и я получил отлично работающую версию OS X на моем ноуте. Почти под все железо я смог найти kextы, кроме дискретной видюхи.

Исследуя возможности, неизвестной на тот момент, системы, я запустил Xcode...с тех пор так его и использую. Первое мое приложение было рисовалка граффити для ВК, очень простое написанное в основном по туториалам и говнокодом.

Получив хоть какой-то опыт написание кода под iOS, я решил пойти заработать на этом денег. (Да, без мака, без айфона и почти без опыта.) 🙂

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

Работать одному над UI было тяжело, спотыкался о всевозможные костыли, почти не спал на протяжении недель, и параллельно боролся с багами и пытался успеть запилить хоть какой-то функционал.

В результате, получил высокий уровень глюкокортикойда в крови, полезный опыт с iOS, относительное понимание бизнеса и немного софт-скиллов.

Мораль сей истории - в начале карьеры не соглашайтесь работать в одного(без более опытного коллеги) и относитесь очень серьезно к WLB. Даже если кажется что ничего не успеваешь, а менеджер не очень доволен, не стоит жертвовать отдыхом.

Стресс реально убивает. Если интересно как он это делает, очень советую книгу Robert Sapolsky - Why Zebras Don't Get Ulcers.

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

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

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

Через какое-то время, я попал в стартап, где мы с товарищем, который отвечал за BE где-то за год запилили продукт. Все было хорошо до тех пор пока продукт не был запущен...

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

Что делать в такой ситуации? Сделать пивот? Поменять стратегию и рынок? Нет! Конечно же виноват дизайн, нужно его поменять, и юзеры попрут.

Юзеры не поперли, а финансы закончились. Так умер не родившись, довольно интересный проект в сфере Интернет обучения.

Мораль тут - стартапы это не просто идея + деньги = продукт, а сложный итеративный процесс с постоянной обратной связью от целевой аудитории. Все мы biased, и очень легко потратить кучу денег на идею, которая никому не интересна (кроме нас самих).

После стартапа захотелось больше стабильности. Так я попал в Topface. @ikimruslan уже писал тут про него, но в двух словах - классный коллектив и очень крутая атмосфера.

Кстати, это первое место, где я попробовал swift. Один из продуктов был написан на одной из самых первых версиях языка.🙂

После Topface бывшие коллеги позвали работать в Сингапур в компанию Bandlab, но это уже тема для вторника. Cheers! 🙂

Вторник


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

После получения оффера, да, получил я его без знания английского, видимо специалист был хороший 😅, я начал осознавать свою роковую ошибку. Договорившись на 3 месяца удаленной работы чтобы закончить все свои дела в Спб, я ускоренно начал заниматься языком.

К большому счастью, Skyeng(НЕ реклама) уже работал, поэтому вопрос куда идти даже не всплывал. За 3 месяца занятий по 3 в неделю, я более менее научился связывать слова в предложения и спрашивать дорогу в булошную.

Но как выяснилось впоследствии для реального общения этого было мало. В Сингапуре локалы говорят на синглише - смеси английского, китайского и малайского. Ла! Понимать такое, особенно по телефону, было весьма затруднительно.

Приходилось адаптироваться и поначалу избегать телефонных разговоров с индусами. Со временем, я даже научился разбирать их акцент и даже собеседовать. 🙂

Английским я продолжил заниматься все 3 года что прожил в Сингапуре. Количество занятий на скайенг я сократил до 2 в неделю, сосредоточившись на грамматике. И добавил занятий с носителем через italki для прокачки разговорного языка.

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

На самом деле, английский стоит учить еще даже по тому, что появляется возможность смотреть фильмы и сериалы в оригинале. Это круто, понимать все то, что не попадает в локализованную версию. Оказывается, британские маты не ограничиваются словом f**k и производными от него. 😀

Если не еще смотрели, то очень советую посмотреть сериал After Life от британского комика Ricky Gervais. (есть на нетфликс) Безумно остроумный и смешной, но в тот же самый момент пронзительно грустный про парня из глубинки у которого умерла жена. (Разумеется в оригинал!)

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

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

Прошу прощения за опечатки, пишу на клаве без кириллицы 😅

Другая причина - до Асашай и европы по 16 часов на самолете, все западные продукты и вино! очень дорогие. А пластиковые малайские помидоры невозможно есть. В Россию навестить близких? Делай огромный крюк через Москву!

Про сам Сингапур не буду очень много писать. Очень чистый город, сногсшибательная архитектура и лучший дуриан что я пробовал! По городу очень приятно гулять, особенно вечером, когда не так убийственно жарко. Но постоянно не оставляет ощущение, что слишком все хорошо.

Невольно вспоминается фраза Уильяма Гибсона, где он называет Сингапур Диснейлэндом со смертной казнью. Эпидемия ковида и локдаун расставили все по своим местам.

Правительство закрывает границу для людей с рабочими визами. В страну могут вернуться только PRы и граждане. Обычных людей в страну не пускают. Премьер министр называет иностранцев обузой для экономики. (Хотя подождите, кто эту экономику построил?)

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

Цель была поставлена, осталось определиться с пунктом назначения. Было 2 варианта - Асашай и Европа. Было решено через Европу по L1 ехать в штаты.

В США, к тому моменту, я был два раза. Отличные национальные парки, хорошая инфраструктура, свободой и демократией веет из каждого холодильника. (Про интервью я расскажу завтра.)

Желание переезжать в штаты пропало после первой же командировки в Менло парк. Два слова - огромная деревня. Не буду перечислять все минусы, но хочется жить все такие в городе. Получив паспорт, платить налоги США, живя в другой стране, тоже звучало не очень привлекательно.

Лондон произвел на нас огромное впечатление. Еще со школы я, конечно же знал, что это зе кэпитал оф грейт британ. Но никогда не рассматривал его как место для жизни. Как же я ошибался.

Почти идеальная погода, высокий уровень жизни, паспорт на уровне США, под рукой вся Европа(до Парижа на поезде 2 часа!), до Москвы 4 часа, ну и конечно же классный британский акцент и не нужно учить местный язык для жизни (как в Нидерландах, Швеции, Франции, etc).

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

Из очевидных минусов это не такой высокий уровень безопасности, как в Сингапуре. В Синге можно совершенно спокойно гулять ночью в пустынных районах и не бояться быть ограбленным. (Привет тотальной слежке и суровым наказаниям)

В Лондоне, у моего коллеги(довольно большого парня) недавно выхватили айфон из рук и свинтили в неизвестном направлении на байке. Я думаю, одного этого примера достаточно для иллюстрации относительно высокого крайм рейта.

Другой минус - это очень весьма поганый сервис. Все работает очень медленно, везде нужно звонить и общаться с поддержкой. При переезде в первую постоянную квартиру, мы заказали подключение интернета в у провайдера Скай. Обещали подключить за 2 недели.

После 3 недель ожидания, выяснилось что они отменили наш заказ и забыли нас об этом уведомить. Предложили оформить новый и подождать еще 2 недели. Были успешно посланы.

Проблема была лишь в том, что это был единственный провайдер, кто предлагал более менее высокую скорость (53mb/s). Пришлось покупать 4G роутер и пользоваться корпоративной симкой, благо на ней есть безлимитный интернет. Скорость была почти такая же как у Ская.

Другая проблема - старое и постоянно неработающее метро. Вместо 20 минут потратить 90 минут с 3 пересадками из за faulty signal? Легко! С утра уехать быстро почти не возможно. Но это я уже наверное сравниваю с Сингом, где очень классный общественный транспорт и метро в частности.

Но к большому счастью, плюсы сильно перевешивают минусы. В Сингапуре почти нереально снять классный викторианский дом с камином и своим огромным садом и лисой в придачу. 🙂
notion image

А в Сингапуре все иностранцы в основном живут в бездушных маленьких бетонных коробках с бассейнами и теннисными кортами.

Например вот с таким видом, как в моем последнем кондо.
notion image

Другой плюс Сингапура, что я заметил, это возможность жить относительно близко к офису, где бы он не находился. Кондо с последних фоток располагался в 20 минутах пешком, либо в 10 минутах на электросамокате от офиса.

Кстати, рядом с моим офисом было вот это, довольно известное, кондо, похожее на поставленные друг на друга хрущевки 😂
notion image

Другой плюс(или минус, как посмотреть) это узаконенное рабство. За ~500 usd в месяц можно нанять помощницу с филиппин и свалить на нее всю грязную работу. Уборка кв, готовка, сидение с детьми, хождение по магазинам, etc.

Честно говоря, я не знаю как на такие деньги можно прожить в Синге. Кто то считает это плюсом, но мне кажется что это рабство 21 века. 500 баксов и один день выходных. 🙁

Для IT в Синге стоит ориентироваться на ЗП от 5к usd минимум. Этого будет хватать на аренду коробки с бассейном и покрытие базовых потребностей.

Ну и на вот такую еду. Слева направо - утиные языки, рыбья шкура, тухлые яйца 😂
notion image

Куриные лапки подойдут на закуску к пиву.
notion image

А вот до таких мест из Сингапура можно добраться на лодке за час.
notion image

К сожалению, в самом Сингапуре покупаться не получится, из-за огромного количества кораблей вокруг острова вода довольно грязная.

По закону, компании в синге обязана предоставить всего одну неделю отпуска в год. Моя компания давала две. Даже так, приходилось очень сильно ужиматься и планировать отпуск так чтобы он выпадал на выходные дни. Особо не попутешествуешь. 😔

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

Если есть вопросы по более конкретным темам, нежели упомянутым сегодня - welcome! Буду рад ответить 🙂 А пока, Cheers!

Среда


Вчера получили несколько вопросов про Лондон и жизнь в нем, попробую создать еще один небольшой тред с ответами в воскресенье, как, собственно, вернусь в Лондон из России 🙂

Wednesday Сегодня расскажу про подготовку и опыт собеседований в FAANG(и не только). Почему из двух офферов выбрал FB, а не Apple. И почему считаю, что FB одна из лучших компаний для работы.

Спустя два года жизни в Сингапуре, мы решили что он плохо подходит на роль страны для жизни. Ужасный климат, проблемы с PR и гражданством, разительно отличающаяся от привычной нам культура. Конечный путь назначения, ну тот момент, был очевиден - США.

Для переезда в США, нам больше всего подходила виза L1(релокация из зарубежного офиса после года работы), так как моя жена тоже планировала устроиться на работу, а H1B/O1 такой чести не предполагают.

Но для релокации из офиса outside USA, сначала нужно было устроиться в такой офис. Поэтому все компании не предлагающие релокацию отпали сами собой. Оставался только FAANG.

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

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

За основу подготовки я взял leetcode.com. Cracking the coding interview стала моей настольной книгой на пару месяцев. В своих занятиях английским я сделал упор на проработку Behavioural questions.

Начал освежать свои знания всякого разного low-level стаффа. И параллельно искать реальные интервью, на которых я смог бы потренировать все свой свежеприобретенные склиллы.

Уверенности не внушал тот факт, что многие компании просто не утруждали себя ответами и игнорировали мои отклики. Другие присылали заготовленные письма с отказами. Зато мой синдром самозванца рос и креп.

Первый мой собес был в китайскую Alibaba. Я, разумеется, многое слышал про нее, но я же не собирался реально туда устраиваться! Тут тебе были и behavioral quiestions, и решение задачек на доске и немного дизайна системы.

Но внезапно меня стали игнорировать(Интересно почему? 🤣), после того как я сказал рекрутеру, что я ценю WLB, предпочитаю тщательное планирование, и поэтому перерабатывать обычно не приходится.

Начало было положено. Не получится с FAANGом, попробую к китайцам еще разок, держа язык за зубами, думал я. 🙂

После этого интервью поперли. Отписался Booking, Bloomberg и пара сингапурских конторок.

Booking был первой компанией, которая после телефонного интервью пригласила меня на онсайт в Амстердам. Все шло хорошо, мне казалось, что мой первый оффер не за горами, но пришел реджект. 🙂 Мое решение дизайна системы назвали overcomplicated и пригласили еще раз через год.

Я уверен, что им не понравился мой ответ на интервью про улучшение в их продукте. За пару месяцев до этого, в Калифорнии букинг списал с меня оплату за жилье 2 раза. Мой банк вернул мне эти деньги обратно на кредитку через пару дней. Букинг через неделю только соизволил ответить.

Разумеется, я предложил улучшить их техподдержку. После этого мне сказали, что это плохое улучшение, так как это невозможно AB-протестировать и интервью закончилось. ☹️

Интервью с блумбергом, тоже прошло относительно хорошо, пока меня не попросили задизайнить BE, API and iOS за 40 минут 😬. К такому повороту я был не готов. Имея только теоретические знания в это области, я смог покрыть только небольшую часть задания не успев добраться до iOS.

Cовет-это окей спрашивать у рекрутера, что ожидать от интервью. Обычно компании заинтересованы максимально снизить уровень стресса у кандидатов. Однако, с блумбергом мне это не помогло, мне сказали что будут обычные iOS задачки но никак не BE high-load на 10к запросов в секунду.

Собес в Google я завалил на этапе телефонного интервью, завалив задачку с pretty-print JSON и парой доп условий. В свое оправдание могу лишь сказать, что интервью было в 6 утра, а так рано обычно думается лишь о том, как можно продолжить дальше спать. 😂

Мой внутренний самозванец торжествовал: “Я же говорил тебе, что FAANG не для тебя, а ты меня не слушал, мог бы и избежать всего этого позора.”

Но тут мне ответили из FB, куда я отправлял резюме парой месяцев ранее...

Телефонное прошло норм, позвали на онсайт. Взял месяц на подготовку. Онсайт прошел хуже чем я ожидал. Пока я ждал ответа, мне написали с Apple, куда меня рефферил мой бывший коллега.

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

Все прошло довольно неплохо. Все интервью были из сингапурского офиса по VC с Купертино. (Вакансия была в Синге) На одном интервью мне даже пришлось объяснять решение hard задачи с литкода устно, потому что рекрутер посадила меня в комнату без whiteboard.

Жизнь и уверенность в своих силах потихоньку налаживались. Я не мог и мечтать и об одном то оффера из FAANG, а в результате получил целых два. Видимо синдром самозванца именно поэтому и называют синдромом, потому что мы сами его выдумываем.

Почему я выбрал FB? На тот момент это было не так очевидно, как сейчас. Обычно для iOS разработчиков кажется, что Apple идеальное место, но это не совсем так.

И на это есть целый ряд причин: - Культура секретности. - Сложность поменять команду. - Жесткая иерархия.

Работая в Apple, чтобы поменять команду, нужно пройти интервью почти с начала. Так как все команды собеседуют к себе сами.

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

В Apple у меня бы почти не было шансов попасть в команду Swift компилятора без должного опыта с языками программирования. Но в FB это возможно.

Мне иногда кажется, что FB это почти полная противоположность Apple. - Открытость только поощряется. - Ты сам решаешь над чем и как работать. - А задача менеджера помочь тебе в этом, а не говорить что делать и как.

Как человеку, кто имеет интерес не только в написании кода, но и в развитии продукта, мне было это особенно интересно. После буткампа, я намеренно не пошел обратно в клиентскую разработку, а решил попробовать что-то новое.

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

А полгода назад, я создал и начал работать над линтером, который работает поверх Swift компилятора. В отличии от swift-format/SwiftLint этот линтер работает не с CST a c full typed AST, и имеет доступ ко всей информации что известна компилятору.

По архитектуре выглядит как clang-tidy, за исключением того, что Swift не имеет открытого API для тулинга, но позволяет отлавливать и исправлять нетривиальные паттерны, которые приводят к регрессу binary size.

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

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

Поменять команду в ФБ можно после года работы в компании. Официально проходить интервью не требуется, но нужно заручиться поддержкой менеджера принимающей стороны.

В итоге, я работаю над тем, что мне безумно нравится, сам решаю что и как делать и определяю дальнейшее направление продукта. А общение с крутыми коллегами из Google, Apple etc. позволяет мне обращать внимание на те области, в которые я раньше никогда бы не полез.

Например в том, как работает DYLD, как сделать запуск приложения сильно быстрее, и найти баг в самом DYLD. (openradar.appspot.com/FB7527953)

Расскажу подробнее про это завтра. Возможно это поможет и вашему приложению стать чуточку быстрее.

Недавно все таки решил получить ачивку, написав и запилив Proposal в Свифт. Вчера поправил PR по замечаниям от @jckarter, скоро поправлю сам питч. Надеюсь, получится добить до конца 🙂 🤞 github.com/apple/swift/pu…

Представить все вышеописанное в компании отличное от FB, лично мне довольно сложно (хоть и возможно). Но я очень рад, что мой внутренний самозванец не победил, и я смог найти почти идеальное место для развития своих навыков.

Тем кому интересно, на литкоде и прорешал около 400 проблем, прошел пару тестовых на interviewing.io и тренировал поведенчиские вопросы на pramp.com. На протяжении пары месяцев, каждый выходные я приезжал в офис, и решал задачи на доске маркером.

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

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

Например бинарник динамической либки содержит секцию __LINKEDIT в которое лежит сериализованное префиксное дерево со всеми доступными символами в бинаре.

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

Компиляторы Swift и Clang просто нашпигованы разными алгоритмами (в особенности деревьями), которые позволяют вашему коду работать относительно быстро.

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

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

Четверг


Thursday Как и обещал, сегодня будет много low-level твиттов про Swift, iOS и алгоритмы.

В больших компаниях, существует 3 ключевые области, регресс в которых, при разработке под iOS, внимательным образом отслеживается, анализируется и оптимизируется. Build speed Binary size Performance

Миграция на Swift, влияет на них всех и к сожалению не самым лучшим образом. Давайте начнем с Build Speed.

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

Компиляторы умеют отдавать статистику по компиляции. (Clang -ftime-trace, Swift -stats-output-dir). Статистика содержит много интересных цифр. Но самое главное - время компиляции разбитое по этапам.

Чтобы примерно сравнить Свифт с языками семейства Clang, мы можем сматчить одинаковый по количеству токенов код и сравнить время чистой компиляции между языками. Конечно, такой подход очень приблизительный, но он показывает порядок цифр.

Самое неожиданное, что чистый билд свифта почти совпадает с C++(особенно где в коде много templates), немного отстает от ObjС, и сильно отстает от чистого С. (Clang PCH/modules были отключены).

Самые медленные фазы у Свифта - type-checking и Name binding. Самое интересное, что name binding может занимать столько же времени как и type-checking в отдельных модулях. Name binding - процесс, когда компилятор пытается найти декларации типов, функций, etc.

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

У Кланга - самые медленные фазы - парсинг хедеров(все языки) и template instantiation(C++/ObjC++). Включение PCH/Modules позволяет немного оптимизировать парсинг хедеров и превратить его в аналог свифтового процесса.

PCH/modules это оптимизации, которые помогают избежать парсинга и сделать поиск деклараций более оптимальным и дешевым(не нужно парсить все сразу). Но может приводить к раздуванию ModuleCache.

 
В двух словах - каждый файл cpp/m/mm компилируется индивидуально, и все импорты раскрываются препроцессором и рекурсивно парсятся каждый раз. Поэтому, если вы не используете PCH/modules кланг делает огромное количество работы каждый раз. Самая жесть с Foundation и UIKit.

Эти два хедера рекурсивно содержат огромное количество токенов. И в некоторых случаях их парсинг может забирать около 99% от общего времени компиляции, даже если используется всего 1 декларация!!!

Убедитесь, что в вашем проекте включен либо PCH, а лучше модульность, и старайтесь удалять не нужные импорты(особенно в самих хедерах!).

Type-checking у свифта это весьма любопытный процесс. Он основан на en.wikipedia.org/wiki/Hindley%E… Очень упрощенно - компилятор строит граф, где ноды это либо известные типы, либо type variable(заглушка под тип). А связи между нодами - это отношения между типами.

Дальше он пытается заменить все type variables на конкретный тип. Если это не получается, система undecidable.

Почему это все очень медленно? Потому что этот алгоритм похож на backtracking, который например используется в поиске с регулярным выражением. А сложность у этого алгоритма экспоненциальная. leetcode.com/tag/backtracki…

Проблема возникает тогда, когда алгоритм должен найти подходящий overload функции для вызова. Например когда используются операторы. (+/-/*/ etc). Так как у них много разных перегрузок для разных типов.

Чтобы найти подходящий(при отсутствие эвристик) компилятор просто будет перебирать варианты пока не найдет тот, что удовлетворит всем условиям. Если в системе есть больше чем один оверлоад и алгоритм не может разделить эту систему на более маленькие, то сложность растет быстро.

Решить большую сложную систему может быть очень трудно, поэтому алгоритм на каждом шаге пытается разбить ее на несколько более маленьких. (Привет Сonnected Сomponents алгоритм)

На самом деле, такие кейсы довольно редки. Core команда сделала невероятную работу по оптимизации этого всего. Почитать подробнее про type checking можно тут github.com/apple/swift/bl… Либо посмотреть классный видос от Robert Widmann. youtube.com/watch?v=IbjoA5…

Почитать про типы с другой стороны можно еще тут medium.com/@slavapestov/t…

Binary size - Очень сложная тема. Но если упростить, то в основном все идет от метадаты и размера самого кода.

Порой, относительно безобидные фичи языка, могут оказаться очень дорогими с точки зрения binary size. Например - протоколы, которые в Swift реализованы как Existential Containers.

Если функция принимает параметр с протоколом, то она не узнает о реальном типе до самого рантайма. А в рантайме в нее придет не тип, а специальный контейнер. Его нужно будет “открыть”. "Эта логика называется boxing/unboxing.

И для того, чтобы вызвать простую функцию нужно в рантайме выполнить много логики. Посмотреть на IR можно тут. (Символ - s6output3FFF1pyAA1P_p_tF) godbolt.org/z/5nr89c

Дженерики реализованы похожим образом в рантайме. Но если нужен performance, то их можно специализировать. В этом случае, будет создана новая функция, которая будет уже работать с конкретным типом. Но это увеличит размер бинаря. Налицо trade-off между Binary size and Performance

Другой трейдофф это Inlining и Outlining. При первом код функций может быть вставлен туда, где он используется, без вызова функции. При втором, наоборот, похожий код из разных функций объединяется в сгенерированные функции, которые вызываются в тех местах, откуда код был взят.

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

Всеми любимые структуры тоже могут очень сильно влиять на размер бинарника. Все идет от value semantic.

Для того, чтобы в рантайме работать с большими структурами, компилятор генерирует value witness table. Это таблица с указателями на функции, которые знают, как инициализировать, скопировать, удалить конкретную структуру.

И все это занимает место в бинаринке. Особенно опасны структуры с высоким уровнем вложенности, когда одна структура содержит поле с другой и тд.

Тут можно увидеть, сколько нужно метадаты и кода чтобы структуры смогли работать. godbolt.org/z/q8fPfT

Чтобы сделать demangle символа: xcrun swift-demangle s6output1AVWV Получите $s6output1AVWV ---> value witness table for output.A

В IR с префиксом @ идут глобальные значения, которые(если не будут удалены во время LTO) пойдут в бинарник. value witness table, nominal type descriptor, full type metadata и field descriptor(если Reflection включен).

Но есть и хорошие новости. В Свифт есть концепт POD/Trivial структур(как в c++). Это такие структуры, все поля которых рекурсивно тоже POD. (Не должны содержать никаких ARC типов).

В этом случае, структуру можно будет просто копировать с memcpy и избежать сложной логики с retain/release ARC типов.

Если тип POD, то сделайте его структурой и сможете выйграть место в бинарнике. Если не POD, и он содержит много разных полей, то в этом случае, будет выгоднее сделать его классом.

Другая очень дорогая фича это ObjC interop. Так как помимо всей свифтовой метадаты нужно будет сгенерировать еще и ObjC метадату, которая занимает очень много места.

Убирайте [@objc](https://twitter.com/objc)/[@objc](https://twitter.com/objc)Members когда это не нужно. И старайтесь избегать случаев, когда [@objc](https://twitter.com/objc) наследуется не явно.

Другая важная привычка, всегда использовать final и минимизировать количество public/open. Все в месте поможет компилятору сделать множество оптимизаций. От dead code elimination до constant propagation и снова DCE.

Если вам важен размер бинарника, обязательно делайте LTO для релизных билдов. Это позволяет делать DCE более эффективно и удалить много не нужной метадаты.

LTO(full/thin) это режим компиляции, когда компилятор производит не объектные файлы а llvm IR, который потом линкуется и подвергается оптимизациям.

За счет того, что оптимизатор видит программу целиком, они может сделать очень много мощных оптимизаций, которые уменьшат размер бинарника и увеличат производительность; После этого из IR будет сгенерирован финальный машинный код.

Делаете LTO?

А вот тут IR для сложной иерархии структур godbolt.org/z/35jsrP

Суббота


Прошлый технический тред не пользовался большой популярность, поэтому этот будет покороче 🙂 Расскажу про performance через призму скорости запуска приложения. (что такое dyld, как он работает, и как можно оптимизировать процесс)

dyld - это динамический лоадер, который позволяет вам использовать динамические либки(frameworks) будь то ваши, либо системные. Это первый piece of software которому передается управление, когда вы тыкаете на иконку любого приложения.

Но сначала немножко о том, как вообще реализована работа динамических либок. Со статическими все просто. Компилятор компилирует объектные файлы из исходного кода, которые могут быть смерджены в архивы, которые по сути я являются стат-либ, по сути просто список символов(функций).

Когда вы используете символ из такой либки, (в C/C++/ObjC) он определяется импортом хедера, но хедеры не содержат реализацию. Поэтому, когда вы финально линкуете свой код, будет запущен статический линковщик - (ld/ld64/lld).

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

Динамические либки немного другой зверь, где поиск символов происходит не на этапе "компиляции" а в рантайме, и отвечает за это dyld.

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

На данный момент вся память процесса пуста. После этого, ядро системы загружает(mmap) в эту память код dyld и передает ему управление.

dyld делает mmap главного бинарника в VM(virtual memory). mmap - это не простое копирование файла в память, а отложенное(lazy). Файл будет скопирован в память по кускам, только тогда, когда кто-то будет из этой VM читать.

В бинарнике есть секция с LOAD Commands, dyld читает ее и теперь знает, что и как делать, какие фреймоворки загружать. Dyld также отвечает за проверку подписи кода, если подпись не валидна, он выдаст ошибку.

Бинарный файл каждой дин-либ он маппит в память процесса, по абсолютно рандомному адресу. Эта технология называется ASLR и используется для главного бинарника тоже. en.wikipedia.org/wiki/Address_s…

В главном бинарнике в сегменте __DATA есть таблица с адресами внешних функций, и весь код что используют эти символы(функции), ссылаются на эту таблицу. Но без dyld там не может быть никаких адресов.

dyld читает секции __LINKEDIT фреймворков, берет оттуда префиксные деревья со всеми символами в либе, и пытается в них найти все те символы, что есть в таблице переходов в главном бинаринике.

После этого, он проставляет в таблицу переходов адреса этих внешних функций. И мы получаем что-то похожее на это func1 -> 0x00001341 func2 -> 0x00001391 func54 -> 0x00003526

После этого, главный бинарник чтобы вызвать внешнюю функцию, загружает сначала ее адрес из этой таблицы, а потом переходит по нему. (indirect function call)

Помимо биндинга внешних символов, dyld так же должен сделать так называемые relocations. Все дело в том, что в коде могут быть глобальные адреса, например, на глобальные переменные или метадату.

Но мы помним, что бинарник был загружен по рандомному адресу(ASLR), и поэтому невозможно предсказать заранее, какой будет адрес.

Поэтому выполняя LoadCommands, dyld ходит по всем таким адресам и добавляет к ним slide. (базовый адрес файла в памяти)

Вот тут и начинаются проблемы с временем запуска приложения. Я думаю, что все знают, что диск гораздо более медленные, чем оперативная память. И как я писал до этого, dyld делает mmap файла в память.

Тоесть чтобы пофиксить глобальный адрес файла в памяти, dyld должен прочитать этот кусок. Но по этому адресу может быть пусто, если это первое чтение. В таком случае случается page fault.

Это такое событие когда MMU не может найти маппинг виртуального адреса в физический, и вызывается исключение, которое обрабатывается ядром системы. А система, знает, что эта страница памяти еще не загружена с диска, поэтому она ее загрузит, и продолжит работу процесса.

MMU - Memory Management Unit. Про Page fault можно почитать на вики. en.wikipedia.org/wiki/Page_fault

Все осложняется тем, что такие адреса лежат не по порядку, а чтение в диска происходит минимум по 4 килобайта(размер memory page на iOS). Поэтому, чтобы сделать все фиксапы, с диска нужно прочитать гораздо больше данных, чем нужно.

Чтобы понять как это все работает, мне очень помогли Instruments, но сам инструмент пришлось реазиловать самому. А для этого выучить язык CLIPS для написания экспертных систем, который придумали в NASA в 1985 году 😅

dyld шлет kdebug сообщения, которые можно прочитать и визуализировать в Instruments.

Выглядит вот так на примере Xcode
notion image

Главный прикол в том, что это работает и на релизных приложениях из AppStore, и на iOS и на apple Watch 🙂

dyld так же настраивает рантайм для работы ObjC, вызывает static initializers(для этого есть инструмент) и делает много других необходимых вещей.

В свифт, кстати, недавно метадата была реализована с помощию относительных указателей, это помогло избавиться от необходимости dyld фиксить глобальные поинтеры, что ускорило время запуска свифт приложений. youtube.com/watch?v=G3bpj-…

Все описанные эффекты с VM, page faults etc вы можете понаблюдать сами в инструментах Disk Usage, VM tracker, Virtual Memory Trace. Тогда вы сможете примерно понять, насколько много времени у вас тратится на IO.

Пока жду завтрак, напишу про оптимизации что эппл делает с dyld 🙂
notion image

Все системные фреймворка, не распространяются как привычные нам фреймворки. Они все лежат в большом файле, что называется dyld_shared_cache, который шарится между всеми процессами.

За счёт этого, загрузка системных фреймворков очень быстрая. Для пользовательских либ, эппл пару версий иос назад выпустила dyld3.

Идея очень простая, много работы что делает dyld можно закэшировать в файлик, но к сожалению это не помогает с проблемами маппинга памяти. По моим данным, это позволяет ускорить время второго и следующих запусков на 10-20%.

При первом запуске, dyld3 строит так называемый closure файл, который и является кэшем и позволяет часть работы делать только при первом запуске. Ребут системы и обновление аппы инвалидирует этот кэш.

Случается это из-за того, что при перезагрузке девайса dyld shared cache маппится по новому адресу и адреса в closure становятся невалидными. Тоже самое и при обновлении аппы, адреса внутри файла изменяются и closure ломается.

При запуске аппы, dyld пытается найти closure со специальным именем которое содержит хэш от system boottime и специальный id, который идентифицирует файл в FS(меняется при обновлении бинарника)

В этом заключался баг, что я нашёл на старых девайсах. Boottime незначительно менялся в микросекундах, так как он высчитывается заново при выходе из режима сна. Из-за этого, хэш название closure менялось, и dyld перестраивал абсолютно валидный кэш каждый раз.

Чтобы понять, что происходит пришлось джейлбрейкнуть айфон, и вручную запускать debug server и коннектить к нему консольный lldb. Так как икс кодный ллдб не позволяет отлаживать dyld.

Apple иногда публикует исходный код dyld, но на тот момент он был старый и не содержал этой логики с подсчетом хэша. Зато я освежил свои знания arm64 ассемблера и терминального lldb. Самое сложное было понять что dyld читает с какого-то адреса зашитого в бинарник.

Потратив пару дней, я нашёл этот адрес в исходных кодах XNU(часть ядра Darwin). Оказалось что это _COMM_PAGE_BOOTTIME_USEC github.com/apple/darwin-x…

Воспроизводится буттайм дрифт очень легко на айфонах до 7. Демопроект лежит тут github.com/maxovtsin/Boot…

Проблема медленного диска проявляет себе не только с dyld, но ещё и с чтением кода с диска. Особенно критично это для функций, которые исполняют на start up path.

Если такие функции в бинарнике располагаются не рядом, то это может ухудшать ситуацию с page faults, что опять-таки приводят к чтению не нужно памяти на старте приложения, когда любая миллисекунда критична.

К счастью, есть способ улучшить лэйаут кода в бинарнике, что улучшит скорость запуска.

Метод довольно простой. Статическому линкеру можно передать файл, который содержит список символов(функций) в специальном порядке. Линкер в этом же самом порядке упорядочит код соответствующий этим символам.

Соответственно, если функции, код которых используется во время запуска, положить рядом, то это улучшит locality кода, и весь код сидеть уместиться в меньшем количестве страниц памяти. Меньше страниц, быстрее скорость чтения с диска.

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

Для генерации order file обычно используется SanitizerCoverage и немного клиентского кода, который записывает порядок вызова функций. Быстрое гугление нашло вот этот инструмент github.com/yulingtianxia/…

Для генерации может ещё использоваться утилита gprof, которая генерирует разные файлы для разных целей. Почитать можно тут, но документ помечен как устаревший, возможно есть более свежие практики. developer.apple.com/library/archiv…

Такие техники называются PGO - Profile guided optimizations. Если не ошибаюсь, Xcode что-то такое поддерживает, но у меня нет рядом ноута чтобы проверить 🙂

У dyld есть ещё апи, которое позволяет загружать динамические библиотеки и резолвить символы на лету. Раньше это помогало отложить часть работы на потом, и загружать либки по мере надобности.

dlopen, dlsym, dladdr. Приложения с таким функционалом можно отправлять в стор. Однако после релиза dyld3, эта возможность потеряла некоторый смысл.

Проблема в том, что для тех библиотек, что загружаются с dlopen, не генерируется closure файл, а собирается in-memory closure, который после перезапуска приложения перегенерируется с нуля. Возможно в будущих версиях dyld, она тоже будет кэшироваться на диск.

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

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

Убедитесь что вам не нужно динамическое поведение и смело меняйте линковку на статическую. Стат линкер, кроме того, сможет удалить не используемый код, если вы включите stripping. Это может быть полезно, если у вас много third-party зависимостей, код из которых не весь нужен.

Завтра ещё напишу тред про жизнь в Лондоне, и почему решил не переезжать в США. И поотвечаю на вопросы 🙂

Воскресенье


Пара вещей, которые я забыл упомянуть касательно Trivial(POD) структур. Thumb rule: Если вы используете тип как контейнер для настроек, любой инфы известной на момент компиляции, скорее всего его можно будет сделать POD. Если как контейнер для инфы из инета, то скорее всего нет

String/Array/Dictionary типы не Trivial, так как это обертки, содержащие внутри поинтер на дату в хипе. А поинтеры требуют специальной логики retain/release, а это ломает тривиальность.

Swift's String type has an interesting cousin called StaticString - which requires that the string is defined at compile time (so it can't use features like interpolation or mutability). One way it could be used is to create non-optional URLs using compile time static strings 👍 https://t.co/NQcQICfqLZ
Однако, для статичных строк можно использовать тип StaticString, который не ломает тривиальность, но может инициализироваться только строковым литералом. twitter.com/johnsundell/st…

Для реализации статических массивов, подходит tuple. В стандартной библиотеке есть приватный тип _FixedArray16. github.com/apple/swift/bl…

В теории его можно скопировать себе в код, и использовать как основу для создания статических массивов, но нужно быть ОЧЕНЬ аккуратным, и обязательно измерять весь выигрыш от таких экспериментов. oleb.net/blog/2017/12/f…

Для проверки типа на тривиальность В stdlib есть приватная функция, которая возвращает Bool. let istypepod = _isPOD(Type.self) Используйте только для дебаг билдов!

Так как тривиальность известна статически, компилятор высчитывает ее значение во время компиляции и его можно подсмотреть в LLVM IR. godbolt.org/z/efjfTa В районе 77-80 строк: store i1 true, i1* %._value, align 1, !dbg !45 true значит что тип Trivial.

А вот тут видно, на сколько вырастает размер Value Witness Table для больших не-POD структур. Для классов не буду комментировать 😂
notion image

Но чем больше в структуре не POD полей, тем более дорогой он будет становиться по сравнению с классами.

Пара takeaways которые очень легко попробовать у себя в проекте: Попробуйте делать LTO для релизных билдов, это может быть очень полезно. Линкуйте весь код статически, по возможности избегая динамических фреймворков. Попробуйте использовать order file или PGO.

Из интернета: > For the Facebook app from 100ms to 500ms+ in startup time. lists.llvm.org/pipermail/llvm…

Тред про Лондон (Дальше будет мое субъективное мнение):

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

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

Жилье очень дорогое. В районе Queen's Park за двушку с приватным садом в доме на 4 кв мы платим 2060 фунтов в месяц лэндлорду + 130 council tax(налог района на уборку и всякие разные расходы) + ~100 за ку + нужно платить садовнику за подстригание кустов около 100 фунтов в месяц.

Итого всего выходит 2400 за жилье в месяц. Продовольствие относительно дешевое, в приличных магазинах вроде Waitrose все очень качественное но и не дешево. По городу есть польские либо русские магазины где можно купить гречу, пельмени, творог.

В арабских магазинах можно за копейки покупать очень вкусные овощи, фрукты и айран которого может быть ~10 разных видов 🙂

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

В Лондоне из крупных компаний присутствует Google, Facebook, Apple, Bloomberg. Получить рабочую визу Tier 2 достаточно просто. В крупных компаниях обычно этим занимаются нанятые консалтинговые компании которые помогают с документами и процессом в целом.

Для получения визы необходимо сдать IELTS минимум на 4 балла в каждой категории (Партнеру сдавать не обязательно). По визе партнера также можно свободно работать.

Нам с женой дали визу сразу на 5 лет, кому то дают на 3 года, в этом случае ее нужно продлевать еще на 2. После 5 лет можно податься на вид на жительство (ILR), после получения которого через год можно податься уже на паспорт, сдав спец тест и IELTS партнеру.

Британский паспорт пока что находится на одном уровне с американским и швейцарским(185 стран без виз), надеюсь так и будет продолжаться дальше.

Работая в FAANG купить дом/квартиру вполне реально (даже freehold). По ценам примерно от 700к фунтов за дом минут в 40-60 минутах от центра на метро/поезде. Для ипотеки нужно 10-20% первоначального взноса. Ставка около 3% на 30 лет. (тут много нюансов)

Общественный транспорт очень хорошо развит, пусть работает иногда и не очень. Очень высокий уровень поездизации и велосипедизации. Сгонять куда-нибудь на поезде взяв с собой велик очень удобно.

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

Почему же все таки решил отказаться от переезда в США? Есть пара очень субъективных моментов. Так как наша цель - получение паспорта, мы рассматриваем страны с точки зрения долгосрочного проживания и скорости получения этого самого паспорта.

Прожив в UK уже год, через 5 лет паспорт уже почти в кармане. Не факт, что в Штатах нам бы понравилось, и пришлось бы начинать процесс получения заново.

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

Хочешь отказаться от гражданства - плоти нолог!

Напряженная ситуация с оружием и шутингами. (я сам люблю оружие, но у них палка определенно перегнута)

Как я уже писал, не очень хочется жить в деревне. Поездка в FB HQ, примерно показала, как выглядит жизнь в долине. Поездки на машине из дома в офис и обратно. Жить в городе, в этом плане, гораздо лучше.

WLB ни к черту, в долине приходится конкурировать с вечно работающими индусами и китайцами. Я хоть и трудоголик, но такое меня тоже не радует 😂

Про близость к Европе и России тоже уже писал. До Парижа 2 часа на поезде, до Спб без багажа за 8 фунтов можно долететь c Wizz.

До Нью Йорка около 4 часов самолетом. На мой взгляд, расположение у Лондона весьма удачное. Калифорния, на мой взгляд, более изолирована.

На этом заканчиваю вещание. Надеюсь, неделя была более менее полезной, ставь лайк, если так. Найти меня можно в IG instagram.com/max.ovtsin/. Всем Cheers!

Ссылки