Godot: Повышение производительности GDScript за счет компиляции Just-in-time (JIT)

Созданный на 5 июн. 2016  ·  105Комментарии  ·  Источник: godotengine/godot

Как насчет принятия JIT-компиляции для GDscripts?

Компиляция JIT не имеет недостатков по сравнению с интерпретируемым кодом (кроме более медленного запуска в некоторых случаях) и имеет много преимуществ, в основном с точки зрения производительности.

Для JIT-компиляции существует по крайней мере два популярных способа кросс-платформенной реализации без написания кода с нуля:

Существует также вариант использования PyPy, JVM или CLR, но они довольно тяжелы, с использованием GNU Lightning или libjit (не уверен, что лицензия совместима с лицензией Godot) и, конечно же, написания генератора кода с нуля (который может занять годы).

Итак, выбор в значительной степени между LLVM IR и DynASM . Оба имеют очень хорошую производительность, но DynASM написан на Lua, а LLVM имеет довольно большой размер (~ 20 МБ), но также предлагает другие функции.

Конечно, компиляция GDScript непосредственно в C++ также может быть приемлемой альтернативой — единственный известный мне игровой движок (если я правильно его понял) — это Enigma , который компилирует свой собственный язык сценариев (EDL) в C++.

Что Вы думаете об этом?

archived discussion feature proposal gdscript

Самый полезный комментарий

Я работаю над компиляцией JIT для своей магистерской диссертации и планирую реализовать что-то подходящее для Godot. До сих пор я в основном только что прочитал много статей по этой теме, особенно о том, как работать с динамически типизированной природой GDScript. Сейчас я начал работать над прототипом, и надеюсь, что скоро мне будет что показать.

Все 105 Комментарий

JIT-компиляция не имеет недостатков по сравнению с интерпретируемым кодом.

Есть одно: портативность…

Но не будет ли реализация статической типизации лучше и проще, по крайней мере, сейчас? Не говоря уже о поддержке C#, которая может в конечном итоге произойти?

@Calinou Переносимость не является большой проблемой, поскольку мы можем просто запустить GDScript, как мы это делаем сейчас, на неподдерживаемых платформах.

статическая типизация также может быть дополнительно изменена

Переносимость @Calinou не представляет большой проблемы, если используются эти библиотеки - они поддерживают множество архитектур (LLVM, вероятно, поддерживает больше архитектур, чем Godot), и, кроме того, интерпретатор всегда можно использовать в качестве запасного варианта (как сказал @bojidar-bg).

LLVM IR на самом деле упростит внедрение статической типизации (на бэкэнде, конечно, GDScript по-прежнему потребуется либо вывод типа, либо явные подписи типов).

Если поддержка C# реализована путем связывания с Mono или CoreCLR, они оба включают JIT, поэтому эта проблема будет решена автоматически. Я могу понять, почему C# (это лучший язык, чем Java, и Unity уже использует его), но для этих целей JVM, вероятно, обеспечит лучшую производительность, чем Mono или CoreCLR (особенно из-за сборщика мусора JVM, который хорошо подходит для игр). так как он ничего не замораживает). После Java 8 я лично не думаю, что большинство людей будут возражать против него вместо C# (поскольку преимущества, предоставляемые C# на данный момент, в основном сводятся к Generics и LINQ, которые не так полезны в разработке игр, что в основном необходимо) , а использование JVM в любом случае дает доступ к множеству более удобных функциональных языков, чем C#, таких как Clojure, Scala и Kotlin.

Кроме того, LLVM позволяет использовать C++ в режиме JIT. Unreal Engine также использует C++ для написания сценариев, так что я не думаю, что это было бы слишком странно (при условии, что сегментные сбои будут более распространены, но GDScript все еще может быть предложен менее опытным программистам). C++14 (по большей части поддерживаемый LLVM) — довольно маленький язык даже по сравнению с Java и C#, если его правильно использовать (конечно, время компиляции велико, а сообщения об ошибках все еще не так хороши).

Я бы поддержал компиляцию JIT для GDscript, если это не означает конец языка (или его соответствующего встроенного редактора), как сейчас,

Мое предложение.

  • Добавьте новый тип статической переменной, который, в свою очередь, можно создать, выполнив что-то вроде
    static var float(5.5) или что-то в этом роде (основное отличие будет заключаться в более высокой производительности и лучшем синтаксическом анализе из-за того, что ожидается, что это будет определенный тип. Этот метод должен позволять использовать статическую типизацию, сохраняя при этом преимущества динамической типизации и устраняя любую потребность для переписывания скриптов, если вы хотите их использовать
  • После этого добавьте процедуру JIT-компиляции, которая преобразует GDscript в C++ за кулисами (т.е. невидима для пользователя), но видима в виде производительности при запуске игры. Идея использования здесь C++ означала бы, что разработчикам Godot не пришлось бы добавлять кучу новых библиотек в исходный код.

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

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

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

На данный момент я не убежден ни в каком подходе.

На Солнце, 5 июн 2016 в 5:37 вечера, Ace-Dragon [email protected] писал:

Я бы поддержал компиляцию JIT для GDscript, если это не означает, что
конец языка (или его соответствующего встроенного редактора) как сейчас,

Мое предложение.

  • Добавьте новый тип переменной _static_, который, в свою очередь, может быть создан с помощью
    способ сделать что-то вроде
    static var float(5.5) или что-то в этом роде (основная разница будет
    более высокая производительность и лучший синтаксический анализ из-за ожидания того, что это
    конкретный тип. Этот метод должен позволять использовать статическую типизацию при
    сохраняя преимущества динамической типизации и устраняя необходимость
    переписывание скриптов, если вы хотите их использовать
  • После этого добавьте процедуру JIT-компиляции, которая преобразует GDscript в
    С++ за кулисами (т.е. невидимый для пользователя), но видимый в форме
    производительности при запуске игры. Идея использования C++ здесь означала бы
    что разработчикам Godot не пришлось бы добавлять кучу новых библиотек в
    источник.


Вы получаете это, потому что вы прокомментировали.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-223836149 ,
или заглушить тему
https://github.com/notifications/unsubscribe/AF-Z2znHs58L-KDgtIjHYYjgHVUGXgwpks5qIzOngaJpZM4IuWZe
.

Haxe использует вывод типов, хотя внутри он безопасен. Лично мне нравится тот факт, что мне не нужно определять тип каждой переменной, но вы можете сделать это, если вам это нужно. Таким образом, у вас есть лучшее из обоих миров: вам не нужно указывать типы (пока компилятор может их понять), а написание «общих»/«шаблонных» методов приводит к гораздо более чистому синтаксису, как в статически типизированных языках. как C++ или C#, но фактически код является типобезопасным, и вы можете указать запрошенный тип, когда захотите его использовать, чтобы сделать код более читабельным.

В Haxe это делается с помощью синтаксиса
var <name> [: <Type>] [=<initial value>]
что выглядит очень странно для типизированных языков, но имеет смысл для такого языка, как GDScript.

Но я понятия не имею, как это влияет на сложность и эффективность JIT-компиляции, я просто хотел указать, что это будет что-то среднее между динамической типизацией, как сейчас, и полной статической типизацией, как в таких языках, как C++.

Я использовал Haxe 1-2 года назад.
Мне нравится, что можно опционально определить тип для переменной и возвращаемой функции, как сказал @Warlaan .

Я бы предпочел стиль объявления типа c++ перед typescript/haxe:

var x = 50
int y = 60
float z =70

Или, может быть, мы могли бы отразить стиль export :

type(int) var y = 60
type(float) z =70

@ bojidar-bg Лично я больше предпочитаю стиль С++ (намного меньше набора текста).

Есть много проблем, которые могут возникнуть из-за размещения типа перед переменной. Go был написан одними из самых опытных разработчиков C (включая Кена Томпсона), и они использовали нотацию Postfix (например, Pascal и Haxe).

Я думаю, что это обсуждение должно быть в отдельном обсуждении. Я не предлагаю голосовать, потому что, конечно, большинство людей привыкли к типизированным языкам, таким как C++, C# и Java, и могли бы голосовать за знакомый синтаксис, а просто для сбора исследовательских материалов и мнений, чтобы найти решение, которое лучше всего подходит для такого языка, как GDScript. (что явно не C-подобно в любом другом аспекте).

Я согласен во всех четырех аспектах:

  1. Синтаксис C++ приятнее читать, но
  2. имхо постфиксная нотация здесь имеет больше смысла
  3. которые я предпочел бы обсудить (не решить большинством)
  4. в другой ветке. Извините, я не хотел захватывать ветку, когда упомянул тему. Вернемся к вопросу JIT-компиляции.

Будет ли реализация JIT через LLVM означать, что Clang будет предпочтительнее GCC. Можно ли скомпилировать LLVM JIT с помощью GCC?

В отличие от Java, Python, C# и т. д., GD Script работает как часть более крупного собственного механизма кода C++. На самом деле и GD Script, и остальная часть движка представляют собой одно целое. Некоторые игровые компоненты написаны на C++, а некоторые — на GD Script. В целом производительность игры зависит от баланса между ними. Написание игры Godot исключительно в скрипте сделало бы ее медленнее, но написание ее в основном через узлы с минимальным скриптом gd сделало бы ее быстрее.

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

Я думаю, что добавление C# на самом деле может быть лучше для движка, чем реализация JIT в GD Script. C#, вероятно, гораздо более оптимизированный и эффективный язык, поэтому, вероятно, такой же быстрый, как GD Script + JIT, но не такой интегрированный, как GD Script.

Чтобы ответить на мой собственный вопрос, я лично не вижу, чтобы это было важным вопросом прямо сейчас. Возможно, в будущем, когда у Godot будут все желаемые функции, разработчики будут просто оптимизировать эти функции.

Будет ли реализация JIT через LLVM означать, что Clang будет предпочтительнее GCC. Можно ли скомпилировать LLVM JIT с помощью GCC?

Да, но тогда LLVM станет зависимостью, поэтому будет разумнее использовать LLVM для всего.

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

Кроме того, есть ли дискуссия о том, почему С# лучше Java или даже С++? Я думаю, что C# — очень хороший язык, но CLR и инструменты определенно уступают JVM (особенно на других платформах, кроме Windows).

@paper-pauper, вы должны сделать сообщение на форуме о Java, JIT, чтобы продолжить это обсуждение. На самом деле я бы предпочел Java > С#. Я думаю, что C# был не выбором, а скорее возможностью, которой решили воспользоваться разработчики.

чтобы в итоге мы решили? будет JIT или нет?

Как насчет возможности компилировать GDScript в статический код C++? Возможно ли это?

@trollworkout : тогда, вероятно, было бы проще и оптимальнее написать игровую логику непосредственно на C ++, не так ли?

@SuperUserNameMan Не обязательно. Во-первых, вы будете использовать C++, а не GDScript, а во-вторых, вам все равно нужно найти способ загрузить двоичный объект как dll и подключить его к движку без необходимости перекомпилировать движок с изменениями ur C++.

@trollworkout Благодаря # 3936 можно было скомпилировать GDScript в C и в конечном итоге загрузить его динамически.

не обязательно переводить скрипт на c++. Права @SuperUserNameMan

или нужен JIT или c# и будет всем счастье

нет, другая тема, связанная с @paper-pauper, на самом деле намного интереснее. Идея состоит в том, чтобы использовать двоичный код C как тип языка сценариев и вызывать код напрямую через отражение, а не интерпретировать его, делая его таким же быстрым, как собственный код C++, я думаю, что это решит проблему ABI. Фактически с этим вам больше не нужен ABI.

В настоящее время я экспериментирую с реализацией GDScript JIT с использованием RPython. Первые (весьма предварительные) результаты многообещающие, но я не знаю, насколько сложно будет интегрировать это в движок.

Есть ли еще интерес к JIT-GDScript или люди больше интересуются C# или статическим GDScript?

Учитывая, что вы на самом деле пробуете возможное решение, я предлагаю попробовать и посмотреть, достаточно ли прироста производительности в играх, чтобы оно того стоило.

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

Для меня важна производительность, я просто не могу выбрать Godot для сценария.
Представление. Но я думаю, что более быстрый язык (Lua, Java, C#) лучше, чем Jit, поскольку Jit не работает на iOS. А компиляция в c++ лишает возможности обновлять игровую логику на iOS

Разве Lua не быстрее только из-за JIT? Или у него тоже есть какая-то компиляция AOT?

Я по-прежнему предпочитаю статически типизированный GDScript (может быть, необязательную систему типизации, такую ​​как TypeScript для JavaScript). Кроме того, транспиляция GDScript -> C++ решила бы множество проблем с производительностью, но я вижу, что это далеко не тривиально.

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

На 20 августа 2016 года в 05:05, Джордж Marques [email protected] писал:

Разве Lua не быстрее только из-за JIT? Или у него есть какой-то AOT
компиляция тоже?

Я по-прежнему предпочитаю статически типизированный GDScript (возможно,
системы, как TypeScript для JavaScript). Также есть GDScript -> C++
транспиляция решила бы много проблем с производительностью, но я вижу это
далеко не тривиален.


Вы получаете это, потому что подписаны на эту тему.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-241175124 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AGVmPZDOcIkO1-6ayDySyPnCfJdRk5Saks5qhm76gaJpZM4IuWZe
.

Статически типизированный GDScript с оптимизированной виртуальной машиной также был бы хорош, есть ли какие-либо планы на этот счет?

Дополнительная система ввода для GDScript, как предложил @vnen, была бы идеальной :)

Исследуя это, я нашел отличный SLJIT. Я пытался выяснить, как я могу интегрировать его, чтобы разрешить JIT-компиляцию, по крайней мере, на функциональном уровне GDScript.

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

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

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

@RandomShaper

Я согласен с тем, что JIT в настоящее время невозможен, но проблема в Variant а не в динамической типизации.

Я экспериментировал с написанием GDScript JIT с использованием RPython, который является основой для PyPy и специально создан для создания JIT для динамических языков.

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

Variant настолько глубоко встроен в Godot, что без него очень сложно обойтись. Мне пришлось бы в основном переопределить все типы данных GDScripts и впоследствии преобразовать их в варианты.

Если у кого-нибудь есть идея, как мы можем обойтись без Variants, быстрый GDScript был бы возможен (мое доказательство концепции GDScript JIT, которое не использовало Variant, было многообещающим. (Глупый) цикл, который в GDscript занимал 20 секунд, был сделано менее чем за секунду, когда JITted)

Что ж, я рассматривал Variant и динамическую типизацию так, как если бы они были одинаковыми.
вещь или как будто одно не может быть без другого.

Но, вероятно, я что-то упустил в дизайне языка.

AIUI Variant позволяет использовать C++ в Godot, как если бы это был язык с динамической типизацией. Особенно в возвращаемом типе для некоторых методов. Я не очень знаком с источником, но я не уверен, что есть чистый способ обойти его.

@brakhane Хотите поделиться своим экспериментом с RPython? Я думаю, всем здесь было бы полезно взглянуть на это :smile:

Однако Variant настолько глубоко встроен в Godot, что без него действительно трудно обойтись. Мне пришлось бы в основном переопределить все типы данных GDScripts и впоследствии преобразовать их в варианты.

Теоретически, какой недостаток в этом (помимо времени, необходимого для его реализации)? Такой вид литья не так уж и дорог, насколько я понимаю.

@paper-pauper посмотрите на PyPy и RPython - Python строго динамически типизирован, а PyPy может очень хорошо ускорить код Python... RPython очень хорош для разработки интерпретаторов (с почти бесплатным JIT)

Была опубликована действительно интересная статья о (в настоящее время в разработке) новом Ruby JIT.

Ключевая часть здесь заключается в том, что Ruby решили реализовать свой JIT, сгенерировав код C и вызвав внешний компилятор C для создания .so, а затем загрузив его. Излишне говорить, что это вызвало много вопросов, и эта статья начинает давать некоторые ответы.

В основном преимущество использования внешнего компилятора (в отличие от интеграции JIT-фреймворка):

  • независимость от одного компилятора (можно использовать GCC или LLVM в зависимости от того, что доступно/лучше)
  • Фреймворки JIT, такие как libGCCJit, молоды, поэтому их API может быть гораздо более хрупким, чем C

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

С точки зрения Godot, этот метод также может быть полезен для статической компиляции GDscript.

Я работаю над компиляцией JIT для своей магистерской диссертации и планирую реализовать что-то подходящее для Godot. До сих пор я в основном только что прочитал много статей по этой теме, особенно о том, как работать с динамически типизированной природой GDScript. Сейчас я начал работать над прототипом, и надеюсь, что скоро мне будет что показать.

@DoctorAlpaca Я почти закончил проверку статических типов для GDScript, я считаю, что это может помочь вам хотя бы немного (вы можете посмотреть мою вилку, если вам интересно, а также # 10630). В конце концов я планировал изучить JIT, но у меня нет с этим опыта.

Некоторое время назад я начал эксперимент с JIT-настройкой GDScript. Я настроил для него кое-что.
Если вы хотите посмотреть... https://github.com/RandomShaper/godot/tree/exp-gdscript-jit-master

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

@RandomShaper Эта установка выглядит неплохо, и после просмотра доступных вариантов я, вероятно, тоже выберу SLJIT. Спасибо за это!

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

Я лично не думаю, что JIT GDScript будет хорошей идеей:

  • JIT не разрешен на мобильных платформах (по крайней мере, на IOS, не уверен насчет Android), которые больше всего нуждаются в такой оптимизации...
  • JIT — это большой беспорядок. Существует несколько подходов (отслеживание и метод JIT), некоторые из них даже не детерминированы :'-(
  • JIT — это БОЛЬШОЙ БОЛЬШОЙ беспорядок. Отладка сложна (включая дампы кода и памяти...), так же как и профилирование, так же как и поддержка кода :'-(
  • JIT — это особый вид беспорядка. JIT-программисты — дефицитный ресурс, поэтому поддерживать такую ​​вещь было бы сложно. Это может привести к усложнению развития GDScript, поскольку интерпретатор и JIT будут тесно связаны.

Проект luaJIT хорошо иллюстрирует эти моменты: проект просто блестящий (действительно хорошая кодовая база, безумно хорошая производительность), он решает реальный сценарий использования, его используют коммерческие компании.
И все же он медленно умирает, поскольку его главный автор отступил, не в силах идти в ногу с новой версией lua (luaJIT остановился на 5.1, утверждая, что добавление целого числа поверх числа с плавающей запятой в язык, сделанный в 5.2, было бы действительно сложно для добавить), даже если сообщество lua имеет средний размер и luaJIT это самый известный проект.

Очевидно, что GDScript на порядок проще, чем Python или даже lua, но бремя создания JIT остается важным, особенно если учесть, что это не основная функция проекта Godot.

Единственным приемлемым решением здесь было бы положиться на llvm, который позаботится обо всех сумасшедших частях и позволит нам просто выводить на него код llvm-ir. Однако, как было сказано ранее, llvm является большой зависимостью (на данный момент мы могли бы почти поставить v8 с Godot и вместо этого написать транспилятор GDScript в Javascript :trollface: ), поэтому необходимо дополнительное исследование, чтобы понять, сколько будет добавлено, прежде чем переходить к следующему шагу. в любом месте. И снова это не будет жизнеспособным решением, по крайней мере, для платформы IOS (черт возьми, Apple!).

Извините, что звучу здесь так мрачно, но я бы не стал публиковать это без предложения ;-)
Я предполагаю, что избиратели опроса понимают под «JIT GDScript» «GDScript, но быстрый, как собственный код». Так что, в конце концов, не имеет значения, что означает правильное достижение этой цели (AOT, JIT, транспиляция + компиляция и т. д.)?

Из того, что я видел в GDScript, действительно классная функция (отсутствие) в том, что это не динамический язык. Нет ключевого слова exec , нет исправлений обезьяны, вы не можете изменять переменную во время отладки и т. д.
Это делает язык подходящим для статической компиляции (и это еще одна причина не использовать JIT). Кроме того, новая система статической типизации должна обеспечивать легкую оптимизацию (сравните с необходимостью делать вывод типов).

Итак, моя идея состояла в том, чтобы начать с создания простого (без оптимизации, просто везде используйте Variant) транспилятора GDScript-to-C, который будет доступен как godot --gdcc <myfile.gd> . Использование существующего компилятора GDScript и API GDNative должно упростить эту задачу.
Затем пользователям остается заниматься остальной частью цепочки инструментов (например, компилировать каждый файл C как .so и заменять исходный файл .gd файлом .gdns, указывающим на .so).

Во второй раз мы могли бы начать задумываться об оптимизации и, что более важно, о способе доставки компилятора в Godot.
Учитывая этот второй момент, tinycc кажется действительно интересным решением, учитывая его малый вес, количество поддерживаемых платформ (x86, amd64, arm, arm64 для linux, win32 и osx), libtcc.h API, которые позволяют его интегрировать. в программу, и это перекрестный режим компиляции. Вдобавок к этому в декабре прошлого года была выпущена версия 0.9.27 (поэтому проект кажется живым ^^).

Наконец, так же, как сейчас мы превращаем файлы .gd в .gdc или .gde во время экспорта, мы могли бы создать новый тип (.gdso ?), который был бы не чем иным, как обычным нативным скриптом (чтобы загрузчик ресурсов GDNative вызывал для него Nativescript). автоматически).

Я согласен, что было бы излишним писать JIT-компилятор для GDScript. Как вы упомянули, компиляция JIT не разрешена на iOS (Android разрешает это), поэтому вам придется либо вернуться к медленной интерпретации на этой платформе, либо написать компилятор AOT (смотрите! теперь вам нужно поддерживать два компилятора, а также переводчик).

Я много раз говорил, что считаю транспилятор GDScript2C лучшим вариантом теперь, когда у нас есть GDNative.

Для двоичных файлов, созданных с помощью модуля mono, вы можете написать компилятор GDScript2IL, и тогда у вас будет компиляция как JIT, так и AOT.

Из того, что я видел в GDScript, действительно классная функция (отсутствие) в том, что это не динамический язык. Нет ключевого слова exec , нет исправлений обезьяны, вы не можете изменять переменную во время отладки и т. д.
Это делает язык подходящим для статической компиляции (и это еще одна причина не использовать JIT).

На самом деле в GDScript вы можете изменять исходный код скрипта во время выполнения.

На самом деле в GDScript вы можете изменять исходный код скрипта во время выполнения.

@neikeq Я искал это, прежде чем писать свой пост, думаю, я не смотрел достаточно внимательно ^^
можете ли вы указать мне пример? (или где это реализовано в кодовой базе)

На самом деле в GDScript вы можете изменять исходный код скрипта во время выполнения.

Не так много, как "изменить". Вы можете установить свойство source_code скрипта с совершенно новым кодом, а затем вызвать reload() для его перекомпиляции. Технически это реализовано и для C#, так как это часть Script API.

@touilleMan ваша идея похожа на то, что обсуждалось в # 11068.

Проблема с транспилированием GDScript или его предварительной компиляцией заключается в том, что даже в системе статических типов все вызовы методов должны быть виртуальными. За параметром Node может скрываться что угодно. Таким образом, вы столкнетесь с такими же проблемами производительности, как и в Java, которая использует JIT для своего байт-кода (частично) по тем же причинам.

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

Кроме того, для динамических вещей вы можете загружать файлы сценариев из любого места на диске и использовать их для всего, для чего вы можете использовать сценарии в своем проекте. Это очень хорошая функция для DLC и/или пользовательского контента (хотя я не знаю, использует ли ее кто-нибудь на самом деле).

@vnen Исходный код на C# используется только для сценариев. Он не скомпилирован на reload .

@touilleMan

Принимая во внимание этот второй момент, tinycc кажется действительно интересным решением, учитывая его малый вес, количество поддерживаемых платформ (x86, amd64, arm, arm64 для linux, win32 и osx), наличие API libtcc.h, позволяющего интегрировать его в программу. и это режим перекрестной компиляции. Вдобавок к этому в декабре прошлого года была выпущена версия 0.9.27 (поэтому проект кажется живым ^^).

Плохая идея, проект скорее мертв, чем жив!

@neikeq

Для двоичных файлов, созданных с помощью модуля mono, вы можете написать компилятор GDScript2IL, и тогда у вас будет компиляция как JIT, так и AOT.

Но это интересный вариант. И, возможно, сравнимы с трудозатратами при добавлении JIT.

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

@DoctorAlpaca, учитывая диспетчеризацию виртуальных методов, я думаю, что при использовании C ++ API все уже так. Итак, ваша цель — сделать GDScript быстрее, чем C++? ;-)
В любом случае, извините, если мой предыдущий пост показался вам в стиле "это бесполезно, вы не должны этого делать".
Опыт гораздо более ценен, чем предположение, поэтому я могу только поддержать вас, кроме того, есть большие шансы, что ваша работа приведет вас к улучшению различных частей движка (исправление ошибок, очистка или даже поиск оптимизации для слишком- динамическая система Variant, чтобы быть быстрой), так что ничего не потеряно!
Кстати, если вы действительно заинтересованы в JIT, я предлагаю вам приехать на Europython (конец июля в Эдинбурге, я буду там ^^), команда Pypy там каждый год, и вы можете делать спринты на Pypy вместе с ними. в течении всех выходных. Обычно людей не так много, и они очень добрые и педагогические, так что это в основном похоже на двухдневный курс JIT с экспертами мирового класса ;-)

@ ret80 Я согласен, что проект tinycc не очень активен. С другой стороны, мы говорим о компиляторе C, наиболее часто используемым стандартом по-прежнему является C99 (при этом C89 все еще действительно присутствует), поэтому после того, как проект станет достаточно стабильным, не нужно делать много изменений (я думаю, мы могли бы сказать, что изменения могут быть сделаны в оптимизации, но это, вероятно, увеличит размер и скорость компилятора, поэтому не является целью этого проекта).
Меня больше всего беспокоит этот компилятор в большей степени из-за поддержки архитектур рук и кросс-компиляции .dll и .dynlib, потому что они являются более экзотической функцией (поэтому потенциально менее протестированы).
Наконец, учитывая, что мы будем использовать этот компилятор с автоматически сгенерированным кодом (всегда те же конструкции и gdnative, что и с одиночной зависимостью), мы должны быть в относительной безопасности, чтобы не столкнуться со скрытыми ошибками, когда проект будет запущен.

@neikeq Я нахожу очень элегантной идею использования среды выполнения моно для JIT! Это уменьшит количество зависимостей (и улучшит количество людей, вовлеченных в модуль mono, что всегда лучше), и убережет нас от выпуска еще одного релиза.

Честно говоря, я бы не JIT, а компилировал AOT. Что-то вроде TerraLang (интерфейс lua для LLVM, предназначенный для динамического создания языков) было бы полезно как для горячей перезагрузки исходного кода, так и для компиляции в двоичные нативные объекты для связывания компиляции (без интерпретации, чистая компиляция LLVM) было бы полезно, поскольку было бы довольно тривиально специализироваться даже на конкретных типах узлов. Виртуальные накладные расходы, вероятно, все еще будут проблемой, но они будут намного быстрее, чем сейчас, особенно после того, как начнет существовать информация о наборе текста. Лично я бы просто встроил LLVM в редактор/компилятор, да, это увеличило бы размер, но это также означает, что сами игры могут оказаться еще меньше, особенно если вы включите объектные файлы и/или исходный код godot в редактор и позволите это LTO все это в меньший и более быстрый конечный результат.

Проект luaJIT хорошо иллюстрирует эти моменты: проект просто блестящий (действительно хорошая кодовая база, безумно хорошая производительность), он решает реальный сценарий использования, его используют коммерческие компании.
И все же он медленно умирает, поскольку его главный автор отступил, не в силах идти в ногу с новой версией lua (luaJIT остановился на 5.1, утверждая, что добавление целого числа поверх числа с плавающей запятой в язык, сделанный в 5.2, было бы действительно сложно для добавить), даже если сообщество lua имеет средний размер и luaJIT это самый известный проект.

Я не уверен, что назвал бы его мертвым, он был разветвлен на github давным-давно и обновляется до современных версий LUA и так далее.

Я нахожу очень элегантной идею использования монорежима для JIT! Это уменьшит количество зависимостей (и улучшит количество людей, вовлеченных в модуль mono, что всегда лучше), и убережет нас от выпуска еще одного релиза.

Тем не менее, это не разрешено для iOS и так далее.

Я все еще думаю, что идея JIT должна быть каким-то образом объединена с GDNative, чтобы GD Script был предварительно скомпилирован как своего рода сторонний код операции C, и GDNative должен был иметь возможность анализировать этот код операции и переводить его в код C++.

@OvermindDL1 Большое спасибо за упоминание TerraLang, похоже, это действительно интересный проект ;-)

Я не уверен, что назвал бы его мертвым, он был разветвлен на github давным-давно и обновляется до современных версий LUA и так далее.

Я не очень близок к сообществу lua, поэтому мое понимание этой темы может быть неправильным. Можете ли вы указать мне разветвленную версию luaJIT, реализующую новые версии lua?

@OvermindDL1 Большое спасибо за упоминание TerraLang, похоже, это действительно интересный проект ;-)

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

Даже с учетом этого, если бы мне пришлось писать собственный внутренний компилятор для GDScript, я бы, вероятно, просто использовал последнюю версию C++ API LLVM. C API стабилен, но C++ API гораздо мощнее, много мощи, необходимой для реализации языка, а не только для сканирования, особенно если вы хотите использовать libclang для полного пакета оптимизации. Тем не менее, TerraLang полностью функционален и в этом смысле, и его значительно проще использовать, если он менее хорошо протестирован и использует более старую версию LLVM (C++ API, хотя они хотят обновить, поэтому, конечно, PR приветствуется).

Я не очень близок к сообществу lua, поэтому мое понимание этой темы может быть неправильным. Можете ли вы указать мне разветвленную версию luaJIT, реализующую новые версии lua?

Я наткнулся на пару интересных на https://github.com/LuaJIT/LuaJIT/network в какой-то момент, он был разветвлен так много раз, и над ним ведется так много работы по разработке на нескольких больших разветвлениях. Я также не очень большой член сообщества Lua, я применяю luajit в нескольких проектах, чтобы получить поддержку сценариев, но сам на самом деле не пишу lua (очевидно, у lua сейчас есть менеджер пакетов, luarocks или что-то в этом роде). Я наткнулся на один или два больших форка luajit, которые также не являются ветвями напрямую из репозитория luajit (поэтому не будут отображаться в этом списке), и я «думаю», что самый большой из тех, что я видел, не был указан в этом списке. .

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

Недавно я начал работать над JIT для GDScript, используя RPython (инструментарий, который PyPy использует для создания своего JIT), и я также рассматривал возможность использования LLVM в качестве альтернативы.

Короче говоря: LLVM не подходит для JIT, я прочитал довольно много сообщений в блогах, пришедших к такому выводу, и мои эксперименты с ним, похоже, также подтверждают это. Есть две основные причины:

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

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

Вот почему я отказался от LLVM (жаль, потому что он мне очень нравится) и работаю с RPython. У него есть плюсы и минусы:

Плюсы

  1. RPython идеально подходит: это система для создания JIT-интерпретаторов для языков с динамической типизацией. Наряду с Python (PyPy) люди написали интерпретаторы PHP, которые оказались намного быстрее официального, Ruby и пользовательских языков.

  2. RPython — отличная система для создания трассировки JIT для языков с динамической типизацией. Для языков с динамической типизацией JIT-трассировка кажется лучшим решением. Внедрение трассировки JIT — тяжелая работа, требующая добавления точек измерения в код без джиттинга. RPython существует уже более 10 лет, над ним работают очень умные люди (о RPython было опубликовано несколько академических статей), и для запуска JIT требуется совсем немного. По сути, он ожидает, что вы напишете интерпретатор байт-кода в RPython и скажете ему, какие байт-коды являются инструкциями перехода, а он сделает все остальное (и делает это тоже довольно хорошо; ранние эксперименты предполагают, что ускорение вычислений тяжелого кода в 40 раз может быть возможным)

  3. JIT-код довольно быстрый и поддерживает серверные части x86, x64 и ARM.

  4. Это активный проект, над которым работает довольно много людей и активное сообщество.

  5. Интерпретатор байт-кода и функции вполне читабельны. Текущая реализация в Variant_op.cpp выглядит ужасно; не поймите меня неправильно, я знаю, почему это выглядит именно так и что это необходимо для хорошей производительности, но взламывать его не очень весело.

Минусы

  1. (Части) Variant.cpp необходимо переписать/продублировать в RPython.

Решение GDScript для динамической типизации, класс Variant, затрудняет создание хорошего JITT-кода. Одна из вещей, которую делает JIT-конвейер, — это замечание, когда функция часто вызывается, например, с параметром int, а затем оптимизируется для этого случая. Если бы мы просто использовали Variant в качестве непрозрачных типов, тесты показывают, что мы могли бы получить улучшение скорости только в 2 раза, потому что все, что мы в конечном итоге эффективно компилируем GDScript в код C++, который эффективно вызывает Variant для каждой отдельной операции, такой как сложение или вычитание.

Поэтому, чтобы получить фактическое использование JIT, по крайней мере, некоторые части Variant должны быть продублированы как код RPython, чтобы JIT знал, что происходит. Это не очень красиво, но, кажется, работает.

  1. RPython создает код C, а не C++. Немного прискорбно, но мы можем заставить это работать, особенно с интерфейсом GDNative.

  2. У нас есть код, написанный на двух языках: C++ и RPython. Однако Godot уже использует SCons, то есть Python, поэтому, строго говоря, мы не добавляем новый язык.

  3. Программировать на RPython неинтересно. По сути, вы в конечном итоге пишете на языке, который несколько более мощный, чем C, но не такой мощный, как C++, и требуется некоторое время, чтобы привыкнуть к тому, какие конструкции Python вы можете использовать, а какие нет.

  4. Процесс преобразования кода RPython в C (который будет полностью рабочим JIT) занимает много времени, и я не преувеличиваю. На этом раннем этапе разработки это занимает «всего» 1-2 минуты, но я ожидаю, что для полной реализации потребуется 20-40 минут. Однако это не так ужасно, как кажется, потому что код C нужно будет генерировать только тогда, когда сам JIT модифицируется, иначе процесс компиляции Godot может просто использовать сгенерированные файлы C.

Я надеюсь, что PoC заработает через 4-8 недель, чтобы получить больше отзывов.

@baekdahl Слишком много читал. GDScript станет типизированным независимо от того, что вы думаете. Решение пришло с самого верха и произойдет в ближайшее время.

@CarlGustavAlbertDwarfsteinYung GDScript становится необязательно типизированным. Большая разница для этой проблемы.

Короче говоря: LLVM не подходит для JIT, я прочитал довольно много сообщений в блогах, пришедших к такому выводу, и мои эксперименты с ним, похоже, также подтверждают это. Есть две основные причины:

Это потому, что JIT должен быть во время разработки. При выпуске релиза должна произойти полная статическая нативная компиляция. LLVM не может быть самым быстрым JIT, но это JIT также будучи в состоянии полностью родные компилировать автономные библиотеки / объекты / программы.

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

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

В любом случае, если GDScript становится типизированным, это не проблема, «динамические» части GDScript в любом случае станут вариантом.

Вот почему я отказался от LLVM (жаль, потому что он мне очень нравится) и работаю с RPython. У него есть плюсы и минусы:

@brakhane Способен ли он к собственной компиляции? Если это не так, то это кажется невозможным, поскольку вы не можете использовать JIT на таких платформах, как iOS, кроме того, JIT во время выполнения замедляет загрузку и буферизацию, тогда как AOT не имеет таких затрат при выпуске.

GDScript становится опционально типизированным. Большая разница для этой проблемы.

Да, но даже «нетипизированные» части являются вариантами, которые достаточно просто представить в LLVM.

Это потому, что JIT должен быть во время разработки.

Тогда это не JIT, а обычная предварительная компиляция.

Способен ли он к нативной компиляции? Если это не так, то это кажется невозможным, поскольку вы не можете использовать JIT на таких платформах, как iOS, кроме того, JIT во время выполнения замедляет загрузку и буферизацию, тогда как AOT не имеет таких затрат при выпуске.

Нет, это JIT. И если iOS не разрешает JIT, там это не сработает. 🤷

Да, но даже «нетипизированные» части являются вариантами, которые достаточно просто представить в LLVM.

Но, как я уже упоминал, вы не получите от этого большого прироста скорости.

GDScript довольно легко скомпилировать следующим образом:

while i<10:
    foo(i)
    i = i + 1

к чему-то эквивалентному следующему C++

// i, foo are Variant
while (i.op("<", Variant_10_const)) {
    foo.call(i);
    i = i.op("+", i, Variant_1_const);
}

(или что-то в этом роде, точно Variant API не помню, давно было)

Но это даст вам только 2-кратное увеличение скорости. Превратить его в while (i<10) {foo.call(new Variant(i)));i += 1} гораздо сложнее.

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

Я хотел бы услышать о них.

Тогда это не JIT, а обычная предварительная компиляция.

В целом JIT может генерировать код не так быстро, как AOT, но это гораздо более быстрое время обработки во время разработки, поэтому он по-прежнему полезен там. Однако я был бы довольно хорош с AOT даже во время разработки, поскольку, если отдельные модули скомпилированы отдельно, это не сильно влияет на скорость (<1 с легко).

Нет, это JIT. И если iOS не разрешает JIT, там это не сработает.

Ах да, тогда это было бы бесполезно. Для iOS требуется либо полностью медленный интерпретатор, либо AOT. Кроме того, AOT загружается быстрее, требует меньше времени выполнения, может выполнять LTO и т. д.

Но, как я уже упоминал, вы не получите от этого большого прироста скорости.

За исключением того, что Typed GDScript сможет выводить «большинство» типов независимо (он может даже украсить вариант в вызовах get_node чтобы создать счастливый путь для ожидаемого типа узла на основе сцены со стандартным резервным вариантом). ). Однако в подавляющем большинстве случаев, даже в нетипизированном коде, он по-прежнему сможет вывести подавляющее большинство типов, что позволит значительно повысить производительность и сократить объем генерируемого кода.

(или что-то в этом роде, точно Variant API не помню, давно было)

Эх, это даже отдаленно не то, как я бы предложил делать это честно. Выполнение нативной операции < через посетителя было бы значительно более производительным, чем тестирование строк в качестве одного из примеров. Однако я не уверен, что этот пример очень действителен, поскольку я полагаю, что тип i будет выведен как целое выше, где он установлен (непоказан код), и даже если это не так, тогда может быть одиночной проверкой типа перед циклом, чтобы проверить, работает ли он с такими интегральными сравнениями, и если он не выдает исключение или ошибку каким-либо другим способом, в противном случае получить интегральное/плавающее значение из варианта (вероятно, привести к чему-то полезному чтобы свести к минимуму генерацию кода, в LLVM уже есть проходы для оптимизации этого), и тогда цикл станет полностью оптимизированным независимо. Вы хотите полностью свести к минимуму время выполнения вариантов и проверять доступ к ним как можно раньше и чаще. Возврат к посетителям также не такой уж и медленный, особенно потому, что большинство посетителей просто выдаст ошибку (например, вызов метода для целого числа).

Но это даст вам только 2-кратное увеличение скорости. Превратить его в while (i<10) {foo.call(new Variant(i)));i += 1} гораздо сложнее.

Это кажется довольно тривиально оптимизируемым? Все было бы вполне родным, вплоть до вызова батута для foo для соответствующей диспетчеризации или около того (простая функция-оболочка для выполнения нетипизированного типа диспетчеризации или ошибки), но даже тогда, если тип foo известен (также возможен вывод, если он не указан явно), тогда это также будет оптимизировано.

Я хотел бы услышать о них.

Помимо встроенной JIT-генерации нативного кода, которая есть в самой LLVM (которая прекрасно работала бы со всем вышеперечисленным и была бы фантастической для редактирования кода во время выполнения во время разработки, при этом сохраняя возможность выполнять AOT для выпусков), которая позволяет напрямую Перевод JIT, есть также библиотеки, такие как pyston, fastlua и другие, построенные на LLVM, которые имеют различные формы отслеживания JIT для улучшения по требованию, которые я лично считаю довольно бесполезными во время разработки, и вы не можете использовать их в надежном выпуске. время в любом случае из-за таких платформ, как iOS, поэтому я не вижу смысла.

@ OvermindDL1 Вы все еще говорите об AOT, который автоматически запускается при сохранении отредактированного GDScript.

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

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

Что мне интересно, так это создание JIT для динамически типизированного GDScript, который работает на ПК. Было бы неплохо, если бы другие люди тоже использовали его, но для меня это не требование. Я ценю предупреждение «вы можете тратить свое время впустую».

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

IIRC, на самом деле это не «<», а скорее заданный параметр Operation.LT.

Это кажется довольно тривиально оптимизируемым?

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

Если вы просто хотите заменить часть интерпретатора кода операции, это не так тривиально (забавный факт: файлы «.gdc» не являются байт-кодом, а больше похожи на токенизированный исходный код, который позже интерпретируется и превращается в байт-код в памяти). На данный момент вы знаете следующее:

  • Есть переменная i, которая сравнивается («меньше чем») с другим вариантом, содержимое которого непрозрачно (хотя вы можете использовать внутренний API, чтобы увидеть, что это целое число со значением 10).
  • Есть еще одна переменная "foo", которая вызывается с i в качестве параметра.
  • мне присваивается новое значение, результат добавления i к другому непрозрачному варианту

Если вы хотите иметь возможность выполнять компиляцию AOT, вам в основном нужно переопределить весь интерпретатор GDScript. Что не так тривиально, как кажется, поскольку значительная часть интерпретатора является частью класса Variant. А Variant — это то, что объединяет godot, даже те части, которые не являются GDScript, сильно зависят от Variant. Поэтому, когда вы начинаете возиться с этим, вы внезапно рискуете нарушить обратную совместимость для интерфейса GDNative.

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

Вы все еще говорите об AOT, который автоматически запускается при сохранении отредактированного GDScript.

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

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

Основываясь на текущей спецификации, можно сделать много выводов, «большинство» кода, как правило, должно автоматически набираться путем вывода. Я очень взволнован этим. (Я поклонник HM-печати, реализовывал ее десятки раз разными способами за эти годы!)

Что мне интересно, так это создание JIT для динамически типизированного GDScript, который работает на ПК. Было бы неплохо, если бы другие люди тоже использовали его, но для меня это не требование. Я ценю предупреждение «вы можете тратить свое время впустую».

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

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

Я не говорю о интерпретации, я говорю о компиляции. Если будет скомпилирован полностью нетипируемый код, то он просто станет вариантами машинного кода (помеченный тип суммы), и любой доступ к нему, например, функция operator< , просто выполнит вызов vistor для этого, что включает в себя один статический переход на основе значения динамического типа, затем выполнение статического кода (который, если это, скажем, целое число, встроен, если это, скажем, пользовательский тип, то это, вероятно, приведет к выполнению виртуального вызова или даже статический вызов, если это большой тип, реализованный непосредственно в варианте, подобном вектору).

Если вы просто хотите заменить часть интерпретатора кода операции, это не так тривиально (забавный факт: файлы «.gdc» не являются байт-кодом, а больше похожи на токенизированный исходный код, который позже интерпретируется и превращается в байт-код в памяти).

Вовсе нет, коды операций вообще не должны существовать к тому времени, когда AOT будет завершен, это должен быть чистый машинный код, уменьшенный до наибольшего из вариантов диспетчеризации машинного кода или до наименьшего из хорошо известного и типизированного эффективного машинного кода.

Есть переменная i, которая сравнивается («меньше чем») с другим вариантом, содержимое которого непрозрачно (хотя вы можете использовать внутренний API, чтобы увидеть, что это целое число со значением 10).

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

Есть еще одна переменная "foo", которая вызывается с i в качестве параметра.

Если тип вызова foo известен, например, в нотации ML, int -> int , то, если тип i также известен и также является целым числом, выполните эффективное машинный код, если тип i неизвестен, проверьте тег типа, если он совпадает, если нет, то ошибка, если да, извлеките значение и выполните эффективный машинный код. Если тип вызова foo неизвестен, но тег типа указывает, что он доступен для вызова (иначе ошибка), то, если тип i известен (для всех определений known здесь я имею в виду, что они известны во время компиляции и, следовательно, распакованы), затем оберните их в вариант (эффективная встроенная установка правильного тега типа и размещение битов i в правильных битах) и выполните вызов варианта к обернутой функции батута, которая затем проверяет аргументы и вызывает реальную внутреннюю функцию (которая была бы вызвана напрямую, если бы тип вызова foo был известен статически).

мне присваивается новое значение, результат добавления i к другому непрозрачному варианту

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

Если вы хотите иметь возможность выполнять компиляцию AOT, вам в основном нужно переопределить весь интерпретатор GDScript.

Это все еще меньше работы, чем создание JIT специально для GDScript, если только вы не оборачиваете существующий JIT (ожидаемые операции которого могут не полностью соответствовать GDScript). АОТ поколение сам по себе не намного сложнее , чем создание переводчицы, особенно на LLVM , как это делает значительно тяжелую работу за вас (оптимизации, LTO, машина генерации коды, отладчик, и т.д. ... и т.д. ...).

Что не так тривиально, как кажется, поскольку значительная часть интерпретатора является частью класса Variant.

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

А Variant — это то, что объединяет godot, даже те части, которые не являются GDScript, сильно зависят от Variant. Поэтому, когда вы начинаете возиться с этим, вы внезапно рискуете нарушить обратную совместимость для интерфейса GDNative.

Именно поэтому варианты должны быть встроены прямо в машинный код. Нет интерпретатора кода операции или чего-то подобного, вместо этого выполняется полная и правильная диспетчеризация посетителей (будь то через локальный переход на основе тега типа или даже через длинный вызов из статического массива (таблица отправки)). Это не только сохранит обратную совместимость, но и позволит интерфейсу GDNative затем напрямую загружать полностью скомпилированные библиотеки AOT (или, если Godot получит возможность LTO движка для интерфейсов GDNative через двоичные объекты, тогда можно будет применить LTO-оптимизацию, что может затем значительно уменьшите размер вывода окончательной программы, поскольку известный неиспользуемый код может быть удален, хотя это будет более поздняя задача).

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

Это, безусловно, нетривиальный объем работы, и на самом деле большая ее часть представляет собой большой объем работы по переводу предыдущего конвейера компиляции GDScript для генерации ассемблерного кода LLVM (хотя для этого у него есть фантастический API) вместо коды операций. Но это вполне выполнимо, и не так долго, как можно было бы ожидать, учитывая языковую реализацию.

Кстати, здесь рассматриваются все операторы Variant: https://github.com/godotengine/godot/blob/d2b75557a5dedf951ee036ca01af4f94bc059069/core/variant_op.cpp#L391 -L392.

Который уже достаточно оптимизирован.

Кстати, здесь рассматриваются все операторы Variant:

Ах, круто, так что он уже использует компьютерный goto, мог бы просто использовать его тогда (хотя, вероятно, хотел бы добавить его и в LLVM, чтобы он мог встраивать известные пути вызовов, когда это возможно, но да, именно что-то вроде этого, это очень много вариантный посетитель и будет использоваться в худшем случае, когда типы вообще не известны.Хотя, если что-то известно о типах, было бы еще быстрее выполнять более прямые оптимизированные вызовы, и, конечно, если все типы известны или существует только одна допустимая ветвь, тогда может быть сгенерирован оптимизированный невариантный машинный код (либо путем извлечения данных из варианта (что более вероятно, путем передачи в разделе приведенных битов), либо просто с использованием данных, уже находящихся в регистрах/стеке/куче, если локально или уже извлечен).

Но, как я уже упоминал, вы не получите от этого большого прироста скорости.

GDScript довольно легко скомпилировать следующим образом:

пока я<10:
фу (я)
я = я + 1

к чему-то эквивалентному следующему C++

// i, foo — вариант
в то время как (i.op ("<", Variant_10_const)) {
foo.вызов(я);
i = i.op("+", i, Variant_1_const);
}

(или что-то в этом роде, точно Variant API не помню, давно было)

Но это даст вам только 2-кратное увеличение скорости.

Так не будет ли в большинстве случаев скомпилированный GDScript быстрее, чем C#?
Лучшая производительность с более простым языком звучит как хорошая сделка для меня.

Так не будет ли в большинстве случаев скомпилированный GDScript быстрее, чем C#?

Скорее всего, нет, учитывая, что ему придется пройти через весь уровень косвенности Variant. Глядя на такие языки, как Wren, кажется, что использование тегов NaN или неупакованных типов более вредно для производительности, чем компиляция вещей в машинный код. C# имеет как неупакованные типы, так и JIT-компиляцию, поэтому компиляция GDScript в машинный код не сделает его волшебным образом быстрее, чем C#.
Обратите внимание, что GDScript уже имеет довольно оптимизированный диспетчер байт-кода, и наличие машинного кода, делающего то же самое, не ускорило бы узкое место.

Что ж, возможно, хорошим решением было бы что-то, что генерирует неоптимизированный код C++ из gdscript (возможно, и файл sconscript), а затем пользователь оптимизирует (это не должно иметь большого значения, поскольку пользователь написал исходный скрипт) и компилирует его. вручную .

Это не идеально, но это уже быстрее, чем делать все вручную.

Если вы собираетесь писать код дважды, почему бы не написать его напрямую на C++?

Это еще одна вещь, связанная с переводом GDScript непосредственно в LLVM, может AOT двоичный файл для любой платформы, а не только для той, на которой вы сейчас находитесь, и для кросс-компиляции.

Эта проблема напоминает мне язык Boo . Язык, вдохновленный Python, работающий на .NET.

Я не знаю, сколько это стоит (я действительно не использовал его, tbh), и проект кажется мертвым (5 лет? Черт...), но я все равно бросаю его.

Я предпочитаю объявление типа Котлина. Это более читабельно

var name:String = "Bob"

@Logmytech Вы

В Ruby появился новый JIT-компилятор: https://bugs.ruby-lang.org/projects/ruby/wiki/MJIT#MJIT-organization

Просто любопытно, это планируется для 4.0?

@girng , есть подозрение, что в Godot 4.0 такого не будет. Но это не совсем :)

Я думаю, что v4.0 может иметь предварительную транспиляцию GDScript-to-C, точно так же, как v3.0 имел предварительную поддержку C#.
(если нам повезет).

Проще сделать поддержку языка ZIG . Перспективный язык. Хотя мне очень нравится GDScript, и если бы он работал быстрее, было бы очень круто. Кстати, было бы здорово, если бы сам файл не был классом; класс был записан в файл, как это делается в других языках. Из-за этой особенности возникают некоторые трудности, которых можно было бы легко избежать.

В Godot 4.0, скорее всего, не будет JIT, но будет улучшена производительность в GDScript.

Я проделал значительную работу по компиляции GDScript в C с помощью GDNative. Я достиг точки, когда я не убежден, что это стоит усилий, чтобы завершить. Теоретически производительность кода AOT может быть на несколько порядков выше, чем при выполнении GDScript, но на практике результаты разочаровывают. Затраты на прохождение уровня GDNative значительны, и без серьезных изменений в самом GDScript скомпилированный код очень хрупок (байт-код GDScript не генерируется таким образом, чтобы его можно было безопасно запускать в более позднем исполнении, поскольку он ожидает, что среда будет статической). , включая положение вещей во внутренних массивах движка).

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

Я работаю над форком, который включает в себя ряд оптимизаций, которые вообще не требуют значительных изменений в GDScript, но уже дают некоторый прирост производительности.

До сих пор я реализовал стандартные структуры данных для оптимизации кода, а также несколько основных проходов оптимизации:

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

В качестве POC я планирую реализовать еще несколько проходов, прежде чем подтолкнуть свою вилку:

  • устранение общего подвыражения
  • вывод типов временных и встроенная арифметика для временных (я также не уверен, что типизированная арифметика будет огромной победой)

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

Вы можете увидеть мой прогресс в вышеупомянутых оптимизациях в моем форке godot, https://github.com/pchasco/godot/tree/gdscript-optimization/modules/gdscript/optimizer.

@pchasco Возможно, вы захотите поговорить с @vnen , который в настоящее время перерабатывает GDScript для Godot 4.0 - IMO, сейчас самое время убедиться, что вы получили такие легко висящие плоды :)

См. https://godotengine.org/article/gdscript-progress-report-writing-tokenizer.

@pchasco Возможно, вы захотите поговорить с @vnen , который в настоящее время перерабатывает GDScript для Godot 4.0 - IMO, сейчас самое время убедиться, что вы получили такие легко висящие плоды :)

См. https://godotengine.org/article/gdscript-progress-report-writing-tokenizer.

http://blog.moblcade.com/?p=114

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

Я не уверен, что у нас когда-нибудь будет JIT, поэтому, возможно, этот вопрос можно закрыть в пользу чего-то другого. Есть идея сделать компиляцию AOT, которой будет проще управлять, чем это.

@vnen Да, название проблемы больше не подходит, если вы читали выше, они сочли, что JIT в основном того не стоит, и вместо этого пошли на целевую оптимизацию в текущем интерпретаторе.

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

http://blog.moblcade.com/?p=114

@pchasco Возможно, вы захотите поговорить с @vnen , который в настоящее время перерабатывает GDScript для Godot 4.0 - IMO, сейчас самое время убедиться, что вы получили такие легко висящие плоды :)
См. https://godotengine.org/article/gdscript-progress-report-writing-tokenizer.

http://blog.moblcade.com/?p=114

Я вижу, вы нашли мой блог!

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

Я не уверен, что у нас когда-нибудь будет JIT, поэтому, возможно, этот вопрос можно закрыть в пользу чего-то другого. Есть идея сделать компиляцию AOT, которой будет проще управлять, чем это.

Я согласен с тем, что JIT не является работоспособным решением, учитывая количество платформ, различное оборудование и объем работы, необходимой для обслуживания. AOT, безусловно, является самым простым и наиболее портативным решением для собственной производительности с GDScript. На самом деле Unity также транспилирует .NET в C++ для своего AOT. Мне было бы интересно внести свой вклад в переписывание GDScript.

@pchasco Я бы посоветовал присоединиться к IRC-каналу #godotengine-devel и связаться там :)

Как я упоминал ранее, очень маловероятно, что мы добавим JIT. В обсуждении есть и другие идеи, но нет вывода.

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

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

@внен
Тогда, пожалуйста, обновите дорожную карту, она вводит людей в заблуждение.

Ну, я даже забыл, что дорожная карта существует, она давно не обновлялась.

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

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

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

в любом случае, своевременный компилятор часто может быть быстрее, чем медленный, устаревший компилятор, система интерпретатора просто и просто плохая, и я чувствую, что лучше вырвать gd-скрипт и просто использовать C++, и просто сделать установка гетто с системой точно в срок

я думаю, что люди не хотят признать, что интерпретатор - дерьмо, потому что люди по какой-то причине не хотят ранить чувства друг друга, но по сравнению с C++ конечный результат хорош, эхх...

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

@ RaTcHeT302 не нужно вносить свой негатив и помнить, что у нас есть Кодекс поведения . Есть планы оптимизировать GDScript разными способами, включая компиляцию в нативный код, но не с помощью JIT. Кроме того, если вы сравните с С++, даже время выполнения JIT будет медленным.

@vnen : Просто сравнялся с C/C++ в тестах производительности, но широко распространено мнение, что здесь задействована магия :smile:

JIT возможен не на всех основных платформах. Лучше сосредоточить усилия
где все могут ими воспользоваться. Заблаговременная компиляция является единственным
100% совместимая технология.

На Солнце, 9 августа, 2020 в 8:46 AM Zireael07 [email protected] писал:

@vnen https://github.com/vnen : просто говорю, что LuaJIT почти готов
там с C/C++ на бенчмарках производительности - но там широко распространено
вера в то, что замешана магия 😄


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-671047838 ,
или отписаться
https://github.com/notifications/unsubscribe-auth/AAUFIAGK3S7RA3DRXW6NUC3R72LCVANCNFSM4CFZMZPA
.

Есть ли у AoT недостатки по сравнению с JIT? Например, как насчет динамически генерируемых или загружаемых скриптов? У JIT не должно быть проблем с их оптимизацией, но AoT?

Кстати, на какой основной платформе JIT невозможен? Я думал, что Java с ее JIT работает почти везде.

Редактировать: О, я думаю, проблема в Apple, которая запрещает использование JIT. Так что это произвольное ограничение поставщика, а не техническое. Рад, что мне не нужно поддерживать какие-либо устройства Apple...

iOS не поддерживает JIT для приложений, распространяемых через App Store.
если только он не использует компонент браузера Apple.

AOT невозможен для динамических скриптов.

На Солнце, 9 авг 2020 в 11:15 monnef [email protected] писал:

Есть ли у AoT недостатки по сравнению с JIT? например что насчет
динамически генерируемые или загружаемые скрипты? У JIT не должно быть проблем
их оптимизация, но AoT?

Кстати, на какой основной платформе JIT невозможен? я думал джава
с его JIT работает почти везде.


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-671064117 ,
или отписаться
https://github.com/notifications/unsubscribe-auth/AAUFIACWWCWDXO4NZ7GCL63R724RHANCNFSM4CFZMZPA
.

iOS, Web и несколько консольных платформ не поддерживают JIT-компиляцию.

Ходят слухи, что AOT теперь выброшен из колоды вместе с JIT. Надеюсь, это неправда. Кто-нибудь, подскажите, это правда?

Вы говорите, что ходят слухи, что AOT больше не будет разрешено на некоторых
устройства? Это буквально невозможно, если я что-то не понимаю?

Ср, 30 сентября 2020 года в 5:47 AM Teashrock [email protected] писал:

>
>

@Calinou https://github.com/Calinou @neikeq https://github.com/neikeq
@reduz https://github.com/reduz @akien-mga
https://github.com/akien-mga


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-701313134 ,
или отписаться
https://github.com/notifications/unsubscribe-auth/AAUFIADVHAXMGBOL3YEDVILSIMEEFANCNFSM4CFZMZPA
.

@pchasco
Нет. Я имею в виду, кто-то сказал мне, что в GDScript не будет ни AOT, ни JIT.

Возможно, он не будет официально поддерживаться, но я могу подтвердить, что новый
Архитектура GDScript, описанная vnen, способна ее поддерживать. Ан
предприимчивый разработчик может реализовать собственный модуль для выполнения AOT для
GDNative C.

Ср, 30 сентября, 2020 в 11:16 AM Teashrock [email protected] писал:

>
>

@pchasco https://github.com/pchasco

Нет. Я имею в виду, кто-то сказал мне, что в GDScript не будет ни AOT, ни JIT.


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/5049#issuecomment-701494412 ,
или отписаться
https://github.com/notifications/unsubscribe-auth/AAUFIACAKZVJQK2CHTDJT63SINKXDANCNFSM4CFZMZPA
.

@pchasco

может быть

Итак, вы не знаете. «Может поддерживать» не означает «поддержит».

@Teashrock Мы не знаем, будет ли GDScript включать компиляцию JIT или AOT в 4.0, но это маловероятно. Может быть, в 4.1 или позже…

Если вам нужна более высокая производительность для обработки чисел (т. е. ваши ограничения ЦП явно связаны с языком сценариев), используйте C#. Но сначала прочитайте туториалы по

@Калину
Просто они говорили о 4.0 в отношении JIT/AOT. А теперь вы говорите мне о 4.1. Почему это? Как это? Кто-нибудь, просто дайте всем четкий публичный ответ, который не будет где-то глубоко на GitHub.

Заранее спасибо.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги