Pull to refresh
20
0
Send message

У меня 10+ лет опыта и максимум на что у меня хватило сил/времени это 4 тривиальненьких фикса в апстрим Линукс ядра которые я даже к резюме не прикрепляю. При этом я туда входил имея неплохой бэкграунд в самом Линуксе и х86 железе.

Чтобы сделать в опенсорсе что-то вменяемое надо делать это фуллтайм и желательно за деньги.

В среднем из 30 откликов отвечают по 2-3

За пределами СНГ ситуация гораздо хуже. Я сейчас нахожусь в ОАЭ и тут на вакансию сениора вполне может набежать 1000+ откликов за неделю (это из того, что показывает Linkedin). В общем, 2-3 ответа из 30 откликов это еще более-менее.

У меня несколько другой опыт. В 9м классе итоговая оценка по физике была между 2 и 3. Причем интерес у меня в то время переодически появлялся, но учитель отбивал все желание как-то заниматься самостоятельно и погружаться.

Подобрали репетитора из местного универа, к 11 классу выиграл региональную олимпиаду и стал призером 4го окружного этапа, на 5й - Всероссийской чуть-чуть не хватило отобраться.

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

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

Иногда попадаются интересные задания. Не так давно делал тестовое на позицию performance engineer в HFT фонд. Нужно было покодить на C/x86 assembly под Линукс с упором на низкое лейтенси. Несмотря на то, что по результатам дальнейших собеседований получил отказ, вполне интересно провел время за выполнением такого задания.

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

Где-то неделю назад собеседовался в Amazon на позицию SDE II, тоже 6 раундов, 5 из которых условный литкод, а 6й систем дизайн. Параллельно на каждом из раундов накидывают поведенческие вопросы, которые соответствуют их Leadership Principles.

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

По результатам довольно смешанные ощущения, вроде нормально пообщались с одной стороны, с другой стороны оффер не сделали.

Как разработчик, потративший больше 5 лет на разработку бэкэнда на Scala typelevel могу сказать что там были свои недостатки с которыми было достаточно тяжело жить:

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

  2. Крайне запутанные хипдампы. Иногда возникают задачи по исследованию утечек памяти и вот тогда без хипдампа обойтись довольно трудно. Рантайм cats-effect, fs2 сильно усложняет этот процесс

  3. Функциональный рантайм cats-effect не дружит с java.util.concurrent. Казалось бы, завернем все в Sync[F] и все у нас будет прекрасно. Нет, не будет. Блокировка нативных потоков влияет на экзекутор который используется рантаймом cats-effect и можно легко схлопотать дедлок так что даже Blocker[F] не всегда спасает. А если еще и fs2 стримы замешаны то дедлок может возникнуть в одном месте, а взорваться может совсем в другом (проверено на практике)

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

Не так давно постучалась hr в личку в линкедине с вакансией разработчика в Apple. Созвонились, поговорили, в итоге выяснилось, что позиция не в сам Apple, а в компанию которая у них на контракте и что процесс будет включать в себя “non trivial test assignment”, 2 собеседования с разработчиками компании контрактора и потом если ок, то меня направят на стандартный пайплайн собеседования уже с непосредственно разработчиками Apple, а это еще 4 этапа. Единственно что хорошо, это то что они сразу обозначили вилку и я вежливо отказался от выполнения тестового.

Администрирование Linux серверов и решение проблем с производительностью инфраструктуры, скалирование системы на более высокие нагрузки, отказоустойчивостью и и тд безусловно вызывают интерес, только вот к работе программиста, особенно в крупных компаниях, это почти не имеет отношения и подобными вещами занимаются SRE/DevOps инженеры.

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

По опыту работы в крупном Энтерпрайзе РФ и зарубежом, работа программиста сейчас даже на уровне Senior это копипаст кода по шаблону и бесконечная война в комментариях кодревью из-за условной скобки, поставленной не на той сторке (к сожалению, по опыту трех разных компаний за крайние 4 года, 99% комментариев к ревью именно такие). Посмотрев на все это я перешел в девопсы и ниразу не жалею.

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

Например, будучи Java разработчиком очень часто пользуюсь линуксовым perf’ом и strace’ом, чтобы, например, посмотреть что происходит внутри Postgres или Redis и почему он работает так как работает в той или иной ситуации. Gdb, чтобы более детально разобраться на какие нативные функции самого JVM HotSpot проксируется nio. Это сильно упростило жизнь при решении проблем с производительностью IO интенсивного приложения.

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

Понятие «чистый код» довольно субъективно и каждый определяет правила написания «чистого кода» у себя на проекте (в компании) сам. За 10 лет в разработке видел диаметрально противоположные подходы которые декларировались как «хорошие практики» написания кода и, соответсвенно, форсились на код ревью. От разработчиков, приходящих в компанию требовалось следование этим практикам. Приведу ряд реальных примеров с которыми столкнулся в реальных проектах сам. Не сказать чтобы я был с ними согласен, но тем не менее от разработчиков требовалось их соблюдение: «Мы не пишем явно try-catch, потому что это слишком низкоуровневая конструкция, вместо этого нужно использовать нашу «масштабируемую» обертку», «мы не используем принцип dependency injection, потомучто они добавляют сложность в проект и усложняет масштабирование», «мы используем Java stream API для повышения читаемости кода», «мы не используем Java stream API для повышения читаемости кода» и т.д.

Вставлю свои 5 копеек: Есть довольно экзотические применения, когда java-компилятор не позволяет писать определенные конструкции, в то время как байткод с похожим смыслом может быть успешно загружен в JVM HotSpot. Понятно, что следующий код не может быть скомпилирован

public class Cl{
    private static final int fld;

    public static void setFinalField1(){
        fld = 5;
    }

    public static void setFinalField2(){
        fld = 2;
    }
}

Однако, если нагенерить байткод, который меняет static final поле за пределами static initializer то JVM HotSpot успешно его загрузит. Для этого в коде JVM HotSpot есть специальные проверки cо следующим комментарием (ссылка на исходники):

// Check if any final field of the class given as parameter is modified
// outside of initializer methods of the class. Fields that are modified
// are marked with a flag. For marked fields, the compilers do not perform
// constant folding (as the field can be changed after initialization).
//
// The check is performed after verification and only if verification has
// succeeded. Therefore, the class is guaranteed to be well-formed.

Наткнулся на этот фрагмент несколько лет назад совершенно случайно, когда разбирался в исходниках JVM HotSpot. Зачем это нужно -- для меня так и осталось загадкой.

Насколько я полагаю речь про x86. В статье явно отражена информация про иерархию кэшей, а что у современных Intel x86 несколько префтчеров на L1d/L2 не указано, как и перфкаунтеры которые им соответствуют. По поводу префетча с определенным паттерном это L1 IP. L1 DCU для последовательных линий. Все таки хотелось бы посмотреть на то насколько активны также L2 spatial и streamer префетчеров в данных бенчмарках.

Посколько сейчас достаточно много разработчиков используют ноутбуки на М1/М2 а все эти префетчеры это сильно x86 специфические истории , хотелось бы небольшого обзора на эту микроархитектуру.

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

По поводу опечаток, исправили, спасибо.

В первоисточнике такого не припоминаю, разве что оговорился...

Да, все так.

Только после того, как прикинули нужно убедиться, что нет грубой ошибки в подсчете. Для этого необходимо сравнить ожидаемое количество микроопераций с каунтером uops_retired.retire_slots. В этом конкретном случае имеем (1 << 31) * 7 = 15032385536; uops_retired.retire_slots = 15 188 534 289. uops_executed.thread будет сильно отличаться из-за micro fusion. uops_issued.any получается несколько больше (предположительно это возникает из-за операций с памятью и misspeculate'ом, который вызван memory disambiguation).
И всё это сделать глядя на C'шный код? ;)


Изначально не было кода на C и я просто хотел разобраться как ведет себя µop cache в различных ситауциях. Например, известный CPU Erratum описывает неожидаемое поведение кэша при наличии jcc в конце 32-байтного региона. Мне хотелось понять, есть ли что-то похожее на моем железе и, собирая метрики для различных случаев, я случайно наткнулся на проседание в retirement bandwidth при 7-ми микрооперациях. Далее, разобравшись почему так происходит, мне стало интересно будет ли это заментым ограничителем по производительности для, скажем, L1d-bound кода. Я рассмотрел пример, который содержит 7 микроопераций в цикле и померил его. Посколько метрика resource_stall.any была на 4 порядка меньше uops_retired.total_cycles стало ясно, что пропускной способности L1D хватало, чтобы не давать переполниться ROB, MOB(SB+LB) и RS. На графиках видно, что при цикле из 12-микроопераций проседания retirement bandwidth не наблюдается (6 + 6 за 2 такта, при пиковой Renamer bandwidth 4 + 4 за 2 такта), поэтому для устранения этой неэффективности достаточно раскрутить цикл один раз, так что он будет содержать 12 микроопераций.

А вот практический пример не особо понятен.


Для оценки раскидывания микроопераций по линиям можно использовать тот же подход, что и в случае с nop-ами. В данном случае добавляеются операции чтения и записи. Документация Intel описывает, что запись состоит из 2-х микроопераций (Store Address, Store Data), однако к данной инструкции применяется Micro Fusion — т.е. в µop cache они будут храниться как одна микрооперация, а при попадании в RS эта микрооперация будет зашедулена на 2 соответствующих порта. (Т.е. Micro Fusion — это когда одна микрооперация шедулиться несколько раз). Также к cmp-jne применяется Macro Fusion, т.е. две инструкции схлопываются в одну микрооперацию и шедулятся один раз. Таким образом, учитывая micro/macro fusion получаем 7 микроопераций в цикле
Все верно, это by design. Я имел ввиду, что если у нас есть чистая функция (взята с потолка):
  def doSth[F[_]: MonadError[?[_], Throwable]](): F[Int] = for {
    i <- Applicative[F].pure(10)
  } yield i + 1


то использовать Try в качестве F не получится, т.к. Try нарушает правила функтора. А вот Either очень даже подойдет:

  doSth[Try]() // не компилируется, нет нужного инстанса, т.к. нарушено правило
  doSth[Either[Throwable, ?]]() //Ok


Это делает использование Try в функциональном как минимум проблематичным.
Например, в языке Scala для этого используется определенный класс Try.

На мой взгляд Try не самый удобный инструмент для обработки ошибок в Scala, см. например github.com/scala/bug/issues/6284. Лично мне больше по душе тайпкласс MonadError, где ошибка параметризована.
1

Information

Rating
Does not participate
Registered
Activity