Большинство тестов языков программирования — это синтетика. Мы сравниваем скорость сортировки массивов, подсчёт аллокаций и другие упражнения, которые почти не встречаются в реальной жизни.

А вот настоящие приложения — редакторы, игры, GUI-интерфейсы, базы данных — живут совсем в другом мире. Они управляют не сырыми числами, а сложными структурами объектов. И если присмотреться, почти все эти структуры сводятся к одной универсальной модели: DOM-подобному графу.


Всё сводится к DOM-у

Откройте любое современное приложение — и вы увидите знакомые сущности и связи.

  • Игровой движок: сцена, акторы, компоненты иерархически вложенные и перекрестно связанные.

  • UI-фреймворк: контролы внутри контейнеров, окон, панелей — иерархия и связи.

  • Редактор документов: страницы, стили, текстовые блоки, абзацы.

  • MVC, MVVM и прочие архитектуры: модели, представления, контроллеры разложенные по контейнерам и подписанные друг на друга.

  • Базы данных: записи и документы, вложенные в таблицы и коллекции и связанные ключами.

Разные области, разные названия, но структура одна и та же.


Универсальная форма данных

Если убрать детали, всё сводится к трём уровням:

Дерево владения

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

Дерево владения
Дерево владения

Сеть перекрестных ссылок

Реальный мир живет не только деревом. Кнопка ссылается на другую форму. GUI-окно хранит ссылку на сфокусированный элемент. Паттерн издатель-подписка весь построен на слабых перекрестных ссылках между произвольными компонентами. Все эти связи не должны владеть объектами и обязаны безопасно разрываться при их удалении.

Сеть перекрестных ссылок
Сеть перекрестных ссылок

DAG неизменяемых ресурсов

Есть еще то, что используется всеми — строки, стили, текстуры, меши, шрифты. Они неизменяемы и переиспользуются без копирования. Они неизменяемы и потому их расшаривание между иерархиями объектов вообще никак не контролируется.
Менять их можно, но только через явное дублирование — классический copy-on-write.

DAG неизменяемых ресурсов
DAG неизменяемых ресурсов

Вот и всё: дерево, сеть и DAG. Эта тройка лежит в основе практически любой организации данных современного приложения.

Как современные языки справляются с этими структурами?


Универсальное тестовое задание

Чтобы сравнить языки честно, нужно одно общее задание. Берём упрощенное приложение — редактор карточек. Вот таких:

Карточки в редакторе
Карточки в редакторе

Модель:

  • Документ содержит несколько карточек.

  • Каждая карточка хранит список элементов:

    • текстовые блоки — со стилями;

    • картинки — хранящие и рисующие битмапки;

    • автоматические коннекторы со ссылками на другие элементы;

    • кнопки, ведущие на другие карточки;

    • группы элементов (вложенные контейнеры).

В будущем мы планируем добавлять новые типы элементов карточек.

Вышеприведенные карточки в памяти выглядят так:

Объекты в памяти
Объекты в памяти

Пример возможной структуры классов:

Возможная структура классов
Возможная структура классов

Какие операции поддерживаем:

  • Создание, копирование, удаление документов, карточек в документах и элементов в карточках.

  • Изменение любых полей любых вышеперечисленных объектов.

  • Для текстовых стилей - копирование, модификация, перевод некоторых текстовых блоков на модифицированный стиль.

  • Проход по перекрестным ссылкам с проверкой на не-оторванность.

  • Удаление карточки из метода элемента этой карточки.

Правила:

  • Документы, карточки и их элементы должны быть изменяемыми.

  • Стили и битмапы — неизменяемые ресурсы, разделяемые между текстовыми блоками и картинками.
    Любое их изменение должно выполняться через copy-on-write.

  • Удаление любых объектов из иерархии не должно вызывать сбоев — даже если где-то в стеке остались ссылки на них.

  • Перекрестные ссылки из коннекторов к другим элементам и от кнопок к карточкам должны автоматически обрываться при удалении любого из двух объектов (кто ссылается - на кого ссылаются).

  • Обращение по оборванной перекрестной ссылке должно контролироваться и пресекаться, но без падения программы.

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

  • Контроль циклов — группу элементов нельзя вставлять в себя, и в свои подгруппы.

  • Контроль единственности владельца. Карточка не может быть вставлена в несколько документов одновременно и не может быть вставлена в один и тот же документ несколько раз. Аналогично с элементами карточек — они должны иметь строго одного владельца — или карточку или группу.

Почему это лучший тест для языка

Если язык умеет описывать такую модель — он готов к реальной разработке. Если нет — никакая скорость и сборщик мусора не помогут.

DOM-подобные структуры проверяют всё сразу:

  • Может ли язык безопасно управлять временем жизни объектов?

  • Поддерживает ли разделяемые ресурсы без утечек?

  • Работают ли слабые ссылки или показывают на мусор?

  • Можно ли копировать объекты, сохраняя топологию связей?

  • Устойчиво ли приложение при произвольных изменениях?

Это не лабораторный эксперимент — это реальная модель поведения живой программы.


Что оцениваем

Критерий

Вопрос

Безопасность памяти

Возможны ли висячие ссылки и краши?

Предотвращение утечек

Всё ли освобождается корректно?

Ясность владения

Понятно ли, кто чем владеет и когда?

Копирование

Сохраняется ли структура связей?

Слабые ссылки

Автоматически ли инвалидируются?

Устойчивость

Переживает ли система произвольные изменения?

Выразительность

Можно ли это описать без боли и избыточности?

Момент обнаружения ошибок

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


Почему это важно

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

DOM-подобные структуры — не про веб и не про XML. Это универсальная форма данных, через которую можно описать почти любую программу.

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

Если умеет — это язык, на котором можно писать реальные системы. Если нет — всё остальное не имеет значения.


Что дальше

В следующей части я протестирую на этом бенчмарке С++ и JavaScript, как примеры языков с подсчетом ссылок+RAII и со сборщиком мусора. Я прошу всех неравнодушных поучаствовать - показать реализацию этого бенчмарка на вашем любимом языке программирования. Особо приглашаются любители Раста - это ваш шанс показать насколько Rust заточен для реальных задач.

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


  1. Xexa
    10.10.2025 04:22

    Т.е к аллокации(выделение и распределение памяти) и сортировке(найти/вставить) вернулись с этим "домом"


  1. knight_of_light_1
    10.10.2025 04:22

    Очередная нейростатья, где афтор поленился даже глупые llm-паттерны убрать, все эти сравнения, метафоры — по-любому это писал живой человек)


  1. TimurZhoraev
    10.10.2025 04:22

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


  1. Dhwtj
    10.10.2025 04:22

    Поскольку, требований к сложности нет, а упор в основном на скорость и безопасность, то Rust лидер.

    GC языки будут тормозить, особенно в 95 перцентиле. С++ требует высокой дисциплины, не страхует от ошибок

    Особо приглашаются любители Раста - это ваш шанс показать насколько Rust заточен для реальных задач.

    Привет


    1. rsashka
      10.10.2025 04:22

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

      Пока.


      1. Dhwtj
        10.10.2025 04:22

        Множественное владение это не проблема а ошибка дизайна.

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

        Классический пример: дерево. Родитель владеет детьми (Rc<Node>), а дети лишь ссылаются на родителя (Weak<Node>).

        Живите теперь с этим


        1. rsashka
          10.10.2025 04:22

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

          Живите теперь с этим :--)


          1. shai_hulud
            10.10.2025 04:22

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


            1. rsashka
              10.10.2025 04:22

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

              Та и не писал бы. Все равно не понятно, к чему относится ваш комментарий, либо вы отвечаете не тому человеку.