В своё время посты на Хабре и Reddit о статически типизированном скриптовом языке Umka вызвали весьма активную дискуссию.
Прошедшие полтора месяца позволили мне избавиться от некоторых заблуждений, развить язык и дать чуть более вразумительные ответы на вопросы публики. Как и следовало ожидать, наиболее серьёзное испытание выпало на долю самой концепции статической типизации. Она осталась в основе языка, но потребовала компромиссов — в частности, для корректной сборки мусора.
Появились первые замеры быстродействия интерпретатора в сравнении с Wren и Python — их результаты внушают оптимизм. Наконец, родился более реалистичный пример использования Umka по его основному назначению, т. е. как встраиваемого языка.
Информация о типах во время исполнения программы (RTTI). Проект начинался с радикального отказа от хранения типов данных при исполнении программы. Вся информация о типах терялась после компиляции в байт-код виртуальной машины. В принципе, статическая типизация позволяет это сделать, а заодно избавляет от странных трюков вроде упаковки данных в NaN, к которой, например, прибегают создатели JavaScript и Wren ради увеличения быстродействия. Однако обнаружились два случая, в которых пришлось использовать RTTI:
- Приведение интерфейсного типа данных к конкретному — прямой аналог «утверждения типа» (type assertion) в Go, а также, отчасти, оператора
dynamic_cast
в C++. Оно требуется и при сборке мусора, содержащегося в данных, приведённых к интерфейсному типу. - Сборка мусора, связанного с динамическими структурами данных вроде списков и деревьев.
Быстродействие. Изначально Umka никак не предназначался для установления рекордов быстродействия. Безразличие публики к медлительности Python наводило на мысль, что скорость вовсе не то качество, которого в первую очередь ожидают от скриптового языка. Однако успех LuaJIT и активная реклама Wren заставили задуматься. После этого меня уже не удивляло, что и ранние публикации про Umka вызвали вопросы о быстродействии, хотя мне по-прежнему интересно, от кого в первую очередь исходит спрос на скорость. От разработчиков игр?
Пока полный набор тестов не готов, я могу поделиться лишь предварительными результатами замеров. В численных задачах (например, задаче многих тел) Umka надёжно опережает Python, а если в задаче активно используется цикл
for
, то Umka даёт выигрыш даже по сравнению с Wren, который позиционируется автором чуть ли не как самый быстрый скриптовый язык после LuaJIT. Наглядным примером служит перемножение больших матриц:Умножение матриц 400 x 400 (AMD A4-3300M @ 1.9 GHz, Windows 7)
Очевидно, в пользу Umka здесь сыграла поддержка традиционных статических массивов и более низкоуровневая организация цикла
for
, не содержащая вызовов методов. Задачи с интенсивной сборкой мусора (например, создание и обход двоичных деревьев) вызывают много сомнений по поводу эквивалентности сравниваемых алгоритмов. Например, известная реализация двоичных деревьев на Python возвращает содержимое узлов россыпью и выглядит так, будто в принципе допускает размещение всего дерева на стеке — вообще без использования кучи и сборки мусора. Однако она, по-видимому, требует динамической типизации и не может быть точно воспроизведена на Umka. Если же потребовать возвращать узлы в виде структур, как в Umka (а за неимением структур приходится требовать объекты), то быстродействие Python сразу же падает в 3-4 раза. Вариант на Umka вдвое отстаёт от первой реализации и вдвое опережает вторую. Какое сравнение корректнее — не знаю.
Взаимодействие с внешним кодом. Коль скоро язык рассматривается как встраиваемый, понадобился более или менее реалистичный пример взаимодействия кода на C и Umka. В нём средствами игровой библиотеки raylib формируется трёхмерная сцена, а наполнение сцены определяется внешним скриптом на Umka. В примере можно найти и вызов функций Umka из кода на C, и вызов функций C из Umka. Статическая типизация языка Umka позволила естественным образом формировать на нём структуры данных, непосредственно воспринимаемые библиотекой raylib.
Пример трёхмерной сцены, содержимое которой задаётся скриптом на Umka
Обобщённые типы и функции (generics). Как только читатель улавливает сходство Umka с Go, пускай даже синтаксическое — следует вопрос о поддержке generic'ов. Работа в этом направлении пока не вышла из стадии обзора подходов. Конечно, хотелось бы воспользоваться предложениями разработчиков Go, однако сосуществование в их головах интерфейсов и «контрактов» всегда отпугивало, как странное дублирование понятий. К удивлению и радости, в только что вышедшей новой редакции черновика «контракты» исчезли — по тем же причинам, о которых размышлял и я. Пока generic'ов в Umka нет, остаётся пользоваться, как и в Go, пустыми интерфейсами
interface{}
.Документация. Полная спецификация Umka ещё в работе, но уже написана грамматика и расширен обзорный тур по основным возможностям языка.
richman5
Tereshkov Автор
Сейчас это C/C++. Другие языки — при наличии совместимого ABI вызова динамических библиотек. Поскольку многие языки хотят так или иначе работать с DLL, это не должно быть препятствием.
richman5
Разумно… Может ваша шутка про интерес игроделов к скорости вашего ЯП не такая уж и шутка.
Вы наверняка много знаете про проблемы и пути развития встраиваемых ЯП. В этом контексте кмк будет очень эффективно (и эффектно) делать пиар для Umka.
Tereshkov Автор
А кстати, как бы вы обозначили эти проблемы, помимо быстродействия? Для большинства программистов проблем, кажется, и вовсе нет, когда под рукой Lua/LuaJIT (с последним только та проблема, что Майк Полл перестал им заниматься).
funny_falcon
Так что у вашего языка есть шанс: прикрутите MirJIT и будете вторым после LuaJIT (а это уже круто. Учитывая, какой противный язык Lua, многие мечтаю о вменяемой альтернативе).
https://github.com/vnmakarov/mir
Tereshkov Автор
Интересный проект, не первый раз о нём слышу. Но беглый просмотр вызывает некоторые сомнения (поправьте, если я заблуждаюсь): документация скудная; нет разобранных примеров; нет готовых дистрибутивов в виде DLL; непонятно, откуда импортируются функции стандартной библиотеки C (нужно тянуть за собой libc/msvcrt?); непонятно, что делать с нетривиальными частями виртуальной машины — например, сборщиком мусора.
richman5
Я не настоящий сварщик, поэтому просто читаю об этом 1, 2, 3, 4, 5
Tereshkov Автор
Спасибо, очень понравилась первая ссылка — взгляд на проблему с другой стороны баррикад. Хотя некоторые утверждения там весьма поверхностные:
На самом деле, начиная с версии 5, Lua перешла от стековой машины к регистровой и получила от этого большой прирост быстродействия. У меня один и тот же численный алгоритм на Lua 5 работал в 2-3 раза быстрее, чем на Lua 4.Добавим сюда всякие задержки на сборке мусора, поиску метода по цепочке наследования — и окажется, что тормозить там есть чему.
richman5
Да, это парадокс какой-то: вроде бы для целей встраиваемого ЯП скорость не должна быть критичной (обычно есть места вовне, где можно и поболее затормозить программу), но это и есть первый вопрос, который задают про эти ЯП ))