Новый план по dlib2

В связи с выходом RTLD, я слегка изменил планы на вторую версию dlib. dlib2 будет по большей части независима от Phobos и основана на RTLD. Это будет по-прежнему библиотека для Windows и POSIX, но появится также поддержка AArch64. Большинство модулей будут вынесены в RTLD, сохраняя, насколько это возможно, обратную совместимость.

Что будет вынесено в RTLD без изменений в первую очередь:

  • Почти все из dlib.core
  • Контейнеры
  • Аллокаторы, арена
  • Базовая математика
  • Пул потоков (возможно, с некоторыми улучшениями)
  • Функции для работы с текстом

Разграничение функций между RTLD и dlib будет не по степени независимости от Phobos, а по степени фундаментальности. Не все функции dlib имеет смысл выносить в RTLD (вследствие спорного дизайна API или ограниченной области применения). Что, скорее всего, так и останется в dlib:

  • Линейная алгебра и вычислительная геометрия (нужны далеко не во всех приложениях)
  • Абстрактные потоки ввода-вывода (архитектурно тяжелые, нужны не всегда)
  • Интерфейс файловой системы (имеет специфический, не очень удобный API; в RTLD будет свой)
  • Работа с изображениями и звуком (специфическая область применения).

Новые фичи dlib2:

  • dlib2.image. Новый API для работы с изображениями, без SuperImage. Старый API будет реализован в пакете dlib поверх нового. Все декодеры графических форматов пойдут в dlib2, также появится декодер DDS. Основная задача пакета — максимальное упрощение прослойки, необходимой для хранения загруженных изображений в памяти и передачи в другие API, а также поддержка сжатых GPU-форматов. Будет использоваться структура, аналогичная TextureBuffer в Dagon. Этот же пакет будет включать функции-хелперы для создания текстур OpenGL ES.
  • Встроенная поддержка OpenGL ES в RTLD позволит создать новый движок холста для рисования векторной графики — dlib2.canvas. Будут реализованы растеризация сплайнов, функции плоттера, вывод изображений, простого текста и т.д. Это будет не полноценный графический движок, но, как минимум, инструмент для визуализации данных, полезный для научных исследований и прототипирования.

RTLD как замена druntime

Я задумал этот проект как простой способ скомпилировать хоть что-то под AArch64, но очень быстро понял, что можно не ограничиваться полумерами и сделать продвинутый рантайм вместо набора заглушек. Знал бы я изначально, какой портал в ад тем самым открываю… Когда я портировал криптографию с C на D, ощущения, как я уже писал, были как будто полез в трансформаторную будку, а тут — как будто изучаешь ядерный реактор!

Под капотом D устроен весьма интересно и очень не просто. Это не C, где все лежит на поверхности. Компилятор D мало что знает о рантайм-семантике языка — он только генерирует машинный код, используя внешние вызовы для всех нетривиальных механизмов. Выделение памяти, проверка границ массивов, классы, RTTI — все это опирается на специальные хуки, которые реализованы в druntime. Следовательно, все это можно переопределить. Если выкинуть druntime и использовать свою библиотеку без GC, то вам придется написать свои реализации нужных вам хуков. Задача это, безусловно, сложная, системная, но я уже много лет занимаюсь подобными вещами, поэтому к созданию RTLD я подошел во всеоружии. Одно потянуло за собой другое, и вот я уже обнаруживаю себя пишущим собственную _d_newclassT, которая реализует встроенный оператор new для классов!

Это дает большую власть, но я все же не замахиваюсь на полное переизобретение druntime. Моя идея состоит в том, чтобы RTLD подменяла его ровно в той степени, чтобы получилось реализовать библиотеку общего назначения уровня dlib и писать соответствующий идиоматичный код. Иными словами, я не планирую ничем заменять GC, но большинство других возможностей языка будут доступны. На сегодняшний день RTLD уже обеспечивает следующее:

  • Классы, интерфейсы;
  • Литералы массивов, а также оператор new для массивов и классов (под капотом — глобальный аллокатор rtld.core.memory, аналогичный dlib.core.memory). Я его, впрочем, не рекомендую использовать, потому что это создает семантическую путаницу — он все равно требует Delete, а это несовместимо с new при компиляции с druntime;
  • Автоматическая проверка выхода за границы массивов;
  • Вывод некоторых распространенных рантайм-ошибок.

Как гласит название, библиотека пригодна для разработки приложений реального времени — в том смысле, что все функции работают за предсказуемое время (druntime опирается на сборку мусора, поэтому в общем случае нельзя полагаться, что вызов функций Phobos не приведет к глобальной блокировке). Если вы пишете обычные приложения, не ограниченные по таймингам и памяти, то вы сможете использовать RTLD ситуативно, для каких-то отдельных задач, не отказываясь в целом от Phobos — обе библиотеки могут благополучно сосуществовать в одной программе. Полный переход на RTLD целесообразен в особых случаях, из которых я выделяю два главных сценария:

  • Системное программирование: если вы пишете низкоуровневую библиотеку с интерфейсом C, которая будет использоваться другими программами — драйвер, сетевой протокол, графический или звуковой API и т.д.;
  • Программирование встраиваемых систем и маломощных компьютеров. Сюда относятся одноплатные ARM-компьютеры вроде Raspberry Pi или Khadas. RTLD имеет минимальный отпечаток памяти, что позволяет использовать ее на самых слабых машинах, имеющих операционную систему.

RTLD — низкоуровневый рантайм для D

Я начал разработку dlib в далеком 2011 году, когда писал на старом добром OpenGL 1.x под 32-битным Linux. Тогда я и не помышлял об альтернативных архитектурах и кросс-компиляции, мне даже в голову не приходило, что у D когда-нибудь появится поддержка такого количества разнообразных платформ. Но к началу 20-х уже стало очевидно, что грядет эпоха ARM, и старичку x86 придется потесниться, а сегодня AArch64 на устройствах мощнее телефона — уже не причуда и экзотика, а обыденная реальность. Стоит это принять и начать уже думать об ARM не как о мобильной, а как о второй десктопной архитектуре. Это необычное положение дел, потому что на десктопе x86 всегда царил монопольно. Но я вообще-то люблю изучать все новое, и для меня как графического разработчика освоение еще одной процессорной архитектуры — интересный вызов. Надо ли прямо сейчас всем кидаться портировать весь софт на ARM? Я думаю, нет, но какие-то базовые штуки, полезные библиотеки, фреймворки — желательно. Вот и я решил не оставаться в стороне.

Хорошая новость состоит в том, что D, в лице компилятора LDC, отлично поддерживает ARM. LDC — это неявный кросс-компилятор, он умеет генерировать код почти под все платформы, поддерживаемые LLVM, и это можно делать на любой хост-системе, не пересобирая тулчейн. ARM существует в разных аппаратно-программных вариантах, я под ним буду подразумевать aarch64-linux-gnu — то есть, платформу с процессором AArch64, ядром Linux и окружением GNU. Проще говоря, установив LDC, вы можете на x86 собирать приложения под десктоп или сервер на базе AArch64, о чем я уже писал в предыдущем посте.

Плохая новость состоит в том, что для компиляции под нестандартные таргеты нужны соответствующие версии Phobos и druntime. Я боюсь представить, что это за квест — собрать их, и не имею никакого понятия о статусе Phobos для AArch64. Мне это даже не очень интересно, потому что Phobos я считаю не самой удачной стандартной библиотекой, я ее почти не использую — она сильно перегружена, половину можно было бы оттуда вынести в дополнительные DUB-пакеты, многие модули откровенно устарели, некоторые функции работают странно, а шаблоны порой не компилируются. Для меня уже привычен хардкорный путь — игнорировать Phobos и писать все свое. К тому же, под маломощными одноплатниками с маленькой памятью вам все равно GC не друг, и лучше все делать на @nogc-компонентах.

Все эти измышления подвели меня к идее создания новой библиотеки общего назначения, более низкоуровневой, чем dlib. Я ее назвал RTLD — Real-Time Library for D. Она основана на модулях, которые я изначально писал для dlib2/dcore, а также на прямых портах из dlib 1.x. В отличие от dlib, это будет настоящая замена Phobos. RTLD предоставляет самую базовую функциональность, работая поверх libc, WinAPI, POSIX, системных вызовов Linux, а также динамически загружаемых функций — X11 и OpenGL ES. По сути, это библиотека для тех, кто хочет писать на D как на C, не используя ничего лишнего. Однако я намеренно не стал делать ее BetterC, в отличие от dcore. Взвесив все «за» и «против», я решил, что от BetterC на десктопе толку мало — от классов никуда не деться, они все равно потребуются на определенном уровне абстракции, поэтому зачем изначально себя их лишать?

https://github.com/gecko0307/rtld

На данный момент RTLD поддерживает Windows и Linux, компилируется под x86_64 и AArch64. Некоторые ее модули платформонезависимы, и их можно использовать в системной разработке, под bare metal.

Текущий список фич:

  • rtld.core — ключевые функции: работа с динамической памятью, загрузчик библиотек, потоки, мьютексы, создание окна
  • rtld.gl— биндинг к OpenGL ES 2/3 и создание контекста (поддерживаются Windows и Linux/X11)
  • rtld.libc — функции библиотеки C
  • rtld.math — элементарные математические функции
  • rtld.random — генератор псевдослучайных чисел
  • rtld.sys.windows — функции WinAPI
  • rtld.sys.posix — функции POSIX
  • rtld.sys.linux — функции Linux
  • rtld.text — текстовые кодировки
  • rtld.time — функции даты-времени, наносекундный таймер.

Закономерен теперь вопрос архитектуры будущей dlib2. Я склоняюсь к мысли отказаться от dcore в пользу RTLD — это будет базовый бэкенд dlib2, реализующий львиную долю функциональности, которая не зависит от GC. К сожалению, полный отказ от GC в dlib невозможен по ряду причин, но я надеюсь сильно сократить его присутствие.