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

Большую часть моей карьеры, как в науке, так и в отрасли, я занимался оптимизацией языков программирования с динамической типизацией. В своей магистерской я работал над простым оптимизирующим JIT для MATLAB. В своей кандидатской я работал над JIT для JavaScript. Сегодня я работаю над YJIT — оптимизирующим JIT для Ruby, который уже перенесён в CRuby.

В рамках моей кандидатской, работая над собственным JavaScript JIT, я читал множество научных статей и постов о JIT-компиляторах для других динамических языков. Среди прочего, я читал об архитектуре HotSpot, Self, LuaJIT, PyPy, TruffleJS, V8, SpiderMonkey и JavaScriptCore. Также мне удалось пообщаться и даже лично встретиться со множеством очень умных людей, занимавшихся разработкой этих проектов.

Одним из поразивших меня аспектов стало то, что проект PyPy оказался в странной ситуации. Разработчики создали продвинутый JIT-компилятор для Python, который мог обеспечивать гораздо более высокие скорости по сравнению с CPython. Судя по всему, многие люди могли бы извлечь пользу из этого роста производительности, но PyPy практически не использовался в «реальном мире». Одной из сложностей для разработчиков оказалось то, что Python — это движущаяся мишень. Регулярно выходят новые версии CPython, всегда добавляя множество новых возможностей, а PyPy не поспевает за ним, всегда оставаясь позади на много версий Python. Если вы хотите, чтобы ваше ПО на Python было совместимо с PyPy, то вам придётся сильно ограничить применяемые возможности Python, а большинство программистов на Python не хочет этим заморачиваться.

Читая о LuaJIT, я выяснил, что его высоко и ценили, и ценят. Многие люди отдают должное его автору, Майклу Пэллу, как потрясающему программисту. LuaJIT обеспечивает большой рост производительности по сравнению со стандартной интерпретируемой версией Lua, и достаточно активно используется в реальных проектах. Однако я снова заметил, что многие программисты на Lua не желают использовать LuaJIT, потому что в язык Lua постоянно добавляют новые возможности, а LuaJIT отстаёт от него на много версий. Это немного странно, учитывая, что Lua — это язык, известный своим минимализмом. Похоже, разработчики могли бы предпринять усилия, чтобы замедлить добавление новых возможностей и/или координироваться с Майком Пэллом, но этого не было сделано.

Почти четыре года назад меня приняли на работу в Shopify, чтобы я занимался Ruby. По какой-то причине сфера Ruby JIT особенно конкурентна: существовало множество проектов по созданию Ruby JIT. TruffleRuby JIT хвастался самыми впечатляющими показателями производительности, но его использовали недостаточно активно. Тому есть практические причины, например, время «разогрева» у TruffleRuby гораздо больше, чем у CRuby, но я снова увидел динамику, схожую с динамикой PyPy и LuaJIT: в CRuby продолжали добавлять возможности, а контрибьюторам в TruffleRuby приходилось активно работать, чтобы угнаться за ними. Даже не было важно, что TruffleRuby может быть намного быстрее, потому что пользователи Ruby всегда рассматривают CRuby как каноническую реализацию, а всё, что не было с ним полностью совместимым, не стоило и рассмотрения.

Надеюсь, вы уже поняли, к чему я веду. Из своего опыта я пришёл к выводу,, что позиционировать свой проект как альтернативную реализацию чего-либо — это проигрышная стратегия. Не важно, насколько вы умны. Не важно, насколько упорно вы трудитесь. Проблема в том, что когда вы начинаете создавать альтернативную реализацию, то подчиняетесь прихотям канонической реализации. Её разработчики контролируют направление развития проекта, а вам остаётся только догонять. В случае JIT-реализаций традиционно интерпретируемых языков присутствует довольно странная динамика, потому что в интерпретаторе новые возможности реализуются намного быстрее. Разработчики канонической реализации могут видеть в вас конкурента, которого они стремятся обогнать. А вы можете оказаться в роли Сизифа.

Почти четыре года назад благодаря поддержке Shopify мы с двумя коллегами приступили к проекту по созданию YJIT — ещё одного Ruby JIT. Ключевая разница заключалась в том, что мы создавали YJIT не как альтернативную реализацию, а напрямую внутри самого CRuby. Из-за этого пришлось пойти на множество архитектурных компромиссов, но самое важное заключается в том, что YJIT может быть изначально полностью совместим с каждой функцией CRuby. Теперь YJIT — это «официальный» Ruby JIT, он используется в Shopify, Discourse, GitHub и других проектах. Если вы сегодня заходили на github.com или в любой магазин Shopify, то взаимодействовали с YJIT. Мы добились гораздо большего успеха, чем любой другой JIT-компилятор Ruby, и самым важным для этого было обеспечение совместимости.

Прочитав это, вы можете подумать, что самый главный урок заключается в старой поговорке «если не можешь победить, присоединись». В каком-то смысле, это так. Я хотел сказать, что если вы начинаете проект, который пытается позиционировать себя как альтернативная, но более совершенная реализация чего-то, то с большой вероятностью окажетесь в ситуации, когда вам постоянно придётся играть в догонялки и жить в тени канонической реализации. Канонический проект продолжает эволюционировать, и у вас нет иного выбора, кроме как следовать за ним, не имея почти никакого права указания направления вашего собственного проекта. Это не весело. Возможно, вам бы больше повезло, если бы объединились с канонической реализацией. Однако это ещё не весь ответ.

В мире Ruby есть Crystal — напоминающий Ruby язык, статически компилируемый с выведением типов. Этот язык намеренно сделан несовместимым с Ruby, разработчики решили отойти от Ruby, но всё равно он добился скромного успеха. Думаю, это любопытно, потому что позволяет взглянуть на вопрос более широко. Пользователи Ruby не любят Crystal, потому что это «почти Ruby, но не совсем». Синтаксически он выглядит как Ruby, но имеет множество мелких отличий и на практике крайне несовместим. Это только сбивает людей с толку, нарушая их ожидания. Вероятно, Crystal повезло бы больше, если бы он изначально не позиционировал себя, как схожий с Ruby.

У Питера Тиля есть поговорка: «Конкуренция — для неудачников». Его основная мысль заключается в том. что если вы не обязаны этого делать, не следует ставить себя в положение, в котором вы вынуждены конкурировать. Мой совет молодым программистам: если, например, вы думаете о создании собственного языка программирования, то не пытайтесь создать подмножество Python или что-то внешне очень близкое к уже существующему языку. Делайте что-то своё. Благодаря этому вы сможете развивать свою систему в своём собственном темпе и в выбранном направлении, не связанные ожиданиями, что ваш язык должен соответствовать по производительности, набору функций или экосистеме библиотек другой реализации.

Закончу я некоторыми оговорками. Сказанное мной выше применимо, если вы находитесь в ситуации, когда есть каноническая реализация языка или системы. Она не относится к сфере, где есть открытые стандарты. Например, если вы хотите реализовать собственный парсер JSON, то у него есть чётко прописанная спецификация, которая относительно мала и эволюционирует не очень быстро. Этой цели вы можете достичь. Также у нас есть ситуация со множеством браузерных реализаций JavaScript. Частично это возможно благодаря наличию внешнего органа по стандартизации, управляющего спецификацией JS, а люди, работающие над стандартом JS, понимают, что JIT-компилируемые реализации критичны для производительности, и соответствующим образом направляют развитие языка. Они не участвуют в соревнованиях по максимально быстрому добавлению множества новых функций.

Комментарии (8)


  1. chernish2
    24.08.2024 07:10

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


    1. morijndael
      24.08.2024 07:10
      +11

      Старая русская традиция, разговор с копипастой переводом...


  1. Andrey_Solomatin
    24.08.2024 07:10

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


    С компиляторами это может и так. А в общем по разному бывает.

    pytest это альтернативная реализация unittest. Полностью совместима поддерживает все фичи. Уже десяток лет сильно впереди.

    OpenSearch альтернатива ElasticSearch. И они достаточно популярны. Авторы нашли свою нишу, в области более подходящей лицензии для бизнеса.


  1. Andrey_Solomatin
    24.08.2024 07:10
    +1

    Ключевая разница заключалась в том, что мы создавали YJIT не как альтернативную реализацию, а напрямую внутри самого CRuby.

    В Питоне тоже решили так сделать. Подробностей пока не изучал.


    1. chernish2
      24.08.2024 07:10
      +1

      Было бы очень интересно узнать!


  1. zoto_ff
    24.08.2024 07:10

    после этих слов в альтернативных решениях начался сущий кошмар...


  1. iShrimp
    24.08.2024 07:10

    Плохая конкуренция лучше, чем хорошая монополия. Mono проигрывает .NET, Firefox всё больше уступает Chrome. Но если бы не фичи конкурентов, не было бы и развития.