Godot: 通过即时 (JIT) 编译提高 GDScript 性能

创建于 2016-06-05  ·  105评论  ·  资料来源: godotengine/godot

为 GDscripts 采用即时 (JIT) 编译怎么样?

与解释代码相比,JIT 编译没有缺点(在某些情况下,除了启动速度较慢之外),并且有许多优点,主要是在性能方面。

对于 JIT 编译,至少有两种流行的方式可以跨平台实现它,而无需从头开始编写任何代码:

也可以选择使用 PyPy、JVM 或 CLR,但这些都比较重,使用GNU Lightninglibjit (不确定许可证是否与 Godot 兼容),当然,从头开始编写代码生成器(可以需要几年)。

因此,选择几乎是在LLVM IRDynASM之间。 两者都有很好的性能,但 DynASM 是用 Lua 编写的,LLVM 占用空间相当大(~20MB),但还提供了其他功能。

当然,将 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 当然仍然需要类型推断或显式类型签名)。

如果通过链接 Mono 或 CoreCLR 来实现 C# 支持,则它们都包含 JIT,因此此问题将自动解决。 我可以理解为什么 C#(它是一种比 Java 更好的语言,Unity 已经在使用它)但是出于这些目的,JVM 可能会带来比 Mono 或 CoreCLR 更好的性能(特别是因为 JVM 垃圾收集器,它非常适合游戏因为它不会冻结任何东西)。 在 Java 8 之后,我个人认为大多数人不会反对它而不是 C#(因为 C# 在这一点上带来的优势主要归结为泛型和 LINQ,两者在游戏开发中都没有那么有用,这主要是必要的) ,并且使用 JVM 可以访问许多比 C# 更简洁的函数式语言,例如 Clojure、Scala 和 Kotlin。

LLVM 也使得以 JITted 方式使用 C++ 成为可能。 虚幻引擎也使用 C++ 编写脚本,所以我认为这不会太奇怪(假设段错误会更常见,但仍然可以将 GDScript 提供给经验不足的程序员)。 如果使用得当,即使与 Java 和 C# 相比,C++14(大部分由 LLVM 支持)也是一种相当小的语言(当然,编译时间很长,错误消息仍然没有那么好)。

我会支持 GDscript 的 JIT 编译,只要它不意味着语言(或其各自的内置编辑器)的结束,就像现在一样,

我的提议。

  • 添加一个新的静态变量类型,然后可以通过执行类似的操作来创建它
    static var float(5.5)什么的(核心区别将是更高的性能和更好的解析,因为它期望它是一个特定的类型。这种方法应该允许使用静态类型,同时保持动态类型的优点并消除任何需要用于重写脚本,如果你想使用它们
  • 之后,添加一个 JIT 编译例程,在后台将 GDscript 转换为 C++(即对用户不可见),但在游戏运行时以性能的形式可见。 在这里使用 C++ 的想法意味着 Godot 开发人员不必向源代码添加一堆新库。

Lua 等动态类型语言中的 JIT 是通过类型推断完成的
从入口点到调用树,尽可能深入。 我是
并不是说这在 Godot 中是不可能的,因为几乎每个条目
输入点。 Godot 中的代码补全做了类似的事情,
这就是为什么它能够猜出这么多类型信息的原因。

然而,在很多情况下,类型并不明显,而
通过猜测树上的 get_node() 调用,代码完成效果很好
正在编辑时,此信息可能会有所不同或在运行时发生变化。
老实说,我不确定类型推断的效率有多高。

为了帮助解决这个问题,允许 GDScript 允许用户
“强制”变量的类型(这在 lua 中是不可能的)。 但是如果你
将要这样做,那么您不妨只进行全静态类型。

我目前不相信任何方法。

2016 年 6 月 5 日星期日下午 5:37,Ace-Dragon [email protected]写道:

我会支持 GDscript 的 JIT 编译,只要它不意味着
现在的语言(或其各自的内置编辑器)的结尾,

我的提议。

  • 添加一个新的 _static_ 变量类型,它又可以由
    做类似的事情的方式
    static var float(5.5) 或其他东西(核心区别是
    由于期望它是一个更高的性能和更好的解析
    具体类型。 此方法应允许使用静态类型,而
    保持动态类型的优势,并消除任何需要
    如果要使用脚本,请重写脚本
  • 之后,添加一个 JIT 编译例程,将 GDscript 转换为
    C++ 在幕后(即对用户不可见),但在表单中可见
    游戏运行时的性能。 在这里使用 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所说的那样为变量和函数返回可选地定义类型。

在 typescript/haxe 之前,我更喜欢 c++ 类型声明风格:

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

或者也许我们可以反映export的风格:

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

@bojidar-bg 就我个人而言,我更喜欢 c++ 风格(打字少了很多)。

将类型放在变量之前会产生很多问题。 Go 是由一些最有经验的 C 实现者(包括 Ken Thompson)编写的,他们使用 Postfix 表示法(如 Pascal 和 Haxe)。

我认为这个讨论属于单独的讨论。 我不提议投票,因为当然大多数人习惯于 C++、C# 和 Java 等类型化语言,并且可以投票熟悉的语法,但只是为了收集研究材料和意见以找到最适合像 GDScript 这样的语言的解决方案(这在任何其他方面显然都不像 C)。

我同意所有四个方面:

  1. C++ 语法更易读,但是
  2. 恕我直言,后缀符号在这里更有意义
  3. 我更愿意讨论(不以多数决定)
  4. 在另一个线程中。 对不起,我提到这个话题的时候并不是要劫持这个话题。 让我们回到 JIT 编译的问题。

通过 LLVM 实现 JIT 是否意味着 Clang 将优先于 GCC 。 是否可以使用 GCC 编译 LLVM JIT?

与 Java、Python、C# 等不同,GD 脚本作为更大的 C++ 本机代码引擎的一部分工作。 事实上,GD 脚本和引擎的其余部分都是一个单元。 有些游戏组件是 C++,有些是 GD Script。 总体而言,游戏性能取决于在两者之间建立平衡。 仅用脚本编写 Godot 游戏会使其变慢,但主要通过具有最少 gd 脚本的节点编写它会使其更快。

所以我的问题是。 这种小小的性能提升真的值得付出努力吗?

我认为 C# 添加实际上可能比在 GD 脚本中实现 JIT 对引擎更好。 C# 可能是更加优化和高效的语言,因此可能与 GD Script + JIT 一样快,但不如 GD Script 集成。

为了回答我自己的问题,我个人认为现在这不是一个重要的问题。 也许在未来 Godot 拥有所有想要的功能时,开发人员会简单地优化这些功能。

通过 LLVM 实现 JIT 是否意味着 Clang 将优先于 GCC 。 是否可以使用 GCC 编译 LLVM JIT?

是的,但是 LLVM 将成为依赖项,因此将 LLVM 用于所有内容会更有意义。

我认为大型游戏的性能提升不会很小。 也许对于街机游戏它不会引起注意,但在具有大量循环和函数调用的游戏中它肯定会。

此外,是否有关于为什么 C# 优于 Java 甚至 C++ 的讨论? 我认为 C# 是一门非常好的语言,但 CLR 和工具肯定不如 JVM(尤其是在 Windows 以外的其他平台上)。

@paper-pauper 你应该在论坛上发表一篇关于 Java、JIT 的帖子来继续这个讨论。我实际上更喜欢 Java > C# tbh。我认为 C# 不是一个选择,而是开发人员决定抓住的一个机会。

所以我们到底决定了? JIT 会不会?

将 GDScript 编译为静态 C++ 代码的能力如何? 这甚至可能吗?

@trollworkout :那么直接用 C++ 编写游戏逻辑可能会更简单、更优化,不是吗?

@SuperUserNameMan不一定。 首先,您将使用 C++ 而不是 GDScript,其次您仍然需要想办法将二进制对象作为 dll 加载并将其插入引擎,而无需使用您的 C++ 更改重新编译引擎。

@trollworkout感谢#3936,最终可以将 GDScript 编译为 C 并动态加载它。

没有必要在 C++ 中翻译脚本。 @SuperUserNameMan权限

或需要 JIT 或 c#,一切都会很高兴

不,@paper-pauper 链接的另一个主题实际上更有趣。 这个想法是使用 C 二进制作为一种脚本语言,并通过反射直接调用代码,而不是被解释使其与 C++ 本机代码一样快,我认为这将解决 ABI 问题。 事实上,有了这个,你就不再需要 ABI 了。

我目前正在尝试使用 RPython 实现 GDScript JIT。 第一个(非常初步的)结果很有希望,但我不知道将它集成到引擎中会有多难。

是否仍然对 JITed GDScript 感兴趣,或者人们对 c# 或静态 GDScript 更感兴趣?

考虑到您实际上是在尝试一种可能的解决方案,我建议您去尝试一下,看看是否有足够的游戏性能提升值得。

我的意思是这样的事情只有在复杂逻辑可以获得相当大的性能增益时才值得追求(否则这种增益可能会更好地通过对语言本身进行一般优化工作来获得)。

性能对我来说很重要,我就是不能选择戈多做剧本
表现。 但我认为更快的语言(Lua、Java、c#)比 Jit 更好,因为 Jit 在 iOS 上不起作用。 并且编译成 c++ 失去了在 iOS 上更新游戏逻辑的可能性

Lua 不是因为 JIT 而更快吗? 还是它也有某种 AOT 编译?

我仍然更喜欢静态类型的 GDScript(也许是一个可选的类型系统,比如 TypeScript 用于 JavaScript)。 也有 GDScript -> C++ 转换将解决很多性能问题,但我可以看到这远非微不足道。

你如何更新ios中的游戏逻辑? 你不能下载脚本
您的游戏并在 ios 上运行它们,所有内容都必须从您的
在应用商店中发布的原始 ipa。 无论哪种方式,您都必须
更新应用程序以获取新逻辑、事件(如果是新脚本或新运行时)
与编译的逻辑。

2016 年 8 月 20 日 05:05,George 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
.

带有优化 VM 的静态类型 GDScript 也不错,有什么计划吗?

@vnen建议的 GDScript 可选类型系统将是完美的:)

在研究这一点时,我发现了很棒的 SLJIT。 我一直在试图弄清楚如何集成它以允许至少在 GDScript 函数级别进行 JIT 编译。

经过几个小时的阅读/思考,我得出的结论是,它没有什么好处,因为最大的瓶颈,现在我已经深入研究了代码,可能是所有Variant东西。

完成我的测试任务将是矫枉过正,所以我放弃了它。 无论如何,我的存储库中有一个包含我最初工作的分支,以防有人希望使用 SLJIT 并将其集成到构建系统中。

因此,我总结自己认为静态类型会消除大量调用和分支,从而显着提高性能的小组。

@RandomShaper

我同意 JIT 目前不可行,但问题是Variant而不是动态类型。

我已经尝试使用 RPython 编写 GDScript JIT,它是 PyPy 的基础,专门用于为动态语言创建 JIT。

我不同意问题是静态类型,如果我们不必使用Variant ,就有可能拥有快速的 GDScript JIT。 Variant确实复制了动态语言的一些功能,不幸的是,这就是 RPython 的 JIT 获得速度提升的部分。 RPython 需要知道它当前有哪些类型,然后可以加速这些部分。

然而, Variant深深嵌入到 Godot 中,没有它真的很难走。 我必须基本上重新实现所有 GDScripts 数据类型,然后转换为 Variants。

如果有人知道我们如何在不使用 Variant 的情况下度过难关,那么快速的 GDScript 是可能的(我的一个不使用 Variant 的概念验证 GDScript JIT 很有希望。在 GDscript 中花费 20 秒的(愚蠢的)循环是JITted 时在一秒钟内完成)

好吧,我将 Variant 和动态类型视为相同
事情,或者好像一个人不能没有另一个。

但可能我在语言设计上遗漏了一些东西。

AIUI Variant使得在 Godot 中使用 C++ 成为可能,就好像它是一种动态类型的语言一样。 特别是在某些方法的返回类型中。 我对源不是很熟悉,但我不确定是否有一个干净的方法。

@brakhane愿意分享您的 RPython 实验吗? 我想这里的每个人都会受益于看看它:微笑:

然而,Variant 深深嵌入到 Godot 中,没有它真的很难。 我必须基本上重新实现所有 GDScripts 数据类型,然后转换为 Variants。

理论上,这样做的缺点是什么(除了实施它所需的时间)? 据我了解,这种铸造并不那么昂贵。

@paper-pauper 看看 PyPy 和 RPython - Python 是严格动态类型的,PyPy 可以加速 Python 代码的速度非常好...... RPython 非常适合开发解释器(几乎免费的 JIT)

一篇关于(目前正在开发中)新的 Ruby JIT

这里的关键部分是 Ruby 选择通过生成 C 代码并调用外部 C 编译器来构建 .so 然后加载它来实现其 JIT。 不用说,这引起了很多质疑,本文开始给出一些答案。

基本上使用和外部编译器(相对于集成 JIT 框架)的优势是:

  • 独立于单个编译器(可以使用 GCC 或 LLVM,具体取决于可用/最佳)
  • 像 libGCCJit 这样的 JIT 框架还很年轻,所以它们的 API 可能比 C 更脆弱

当然,这需要采取很多预防措施(文章基准将原型与生产就绪项目(如 JRuby)进行比较),但它表明,如果您使用预编译头文件等巧妙的优化,使用外部编译器可以可行的解决方案。

从 Godot 的角度来看,这种技术对于提供 GDscript 的静态编译也很有用。

我正在为我的硕士论文进行 JIT 编译,并计划实施适合 Godot 的东西。 到目前为止,我大多只是阅读了很多关于该主题的论文,特别是关于如何处理 GDScript 的动态类型特性。 我现在已经开始制作一个原型,希望能尽快展示一些东西。

@DoctorAlpaca我几乎完成了

前段时间我开始了一个关于 JIT-ting GDScript 的实验。 我确实为它打下了基础。
如果你想看看...... https://github.com/RandomShaper/godot/tree/exp-gdscript-jit-master

@vnen实际上,我很高兴看到静态类型可以带来多大的性能差异,而不是我计划进行的类型分析。 由于我还必须为我的硕士学位制作一些实际的文本,我认为我不会比原型做得更多,在它上面会有很大的改进空间。

@RandomShaper该设置看起来不错,在查看了可用选项之后,我可能也会选择 SLJIT。 感谢那!

2018 年 5 月的民意调查结果已经出炉,这一提议似乎是路线图的优先事项。

我个人认为 JIT GDScript 不是个好主意:

  • 移动平台上不允许 JIT(至少在 IOS 上,不确定 Android),这是最需要这种优化的平台......
  • JIT 是个大麻烦。 有多种方法(跟踪与方法 JIT),其中一些甚至不是确定性的:'-(
  • JIT 是一大堆乱七八糟的东西。 调试很复杂(涉及到 jited 代码和内存的转储......),分析也是如此,维护代码也是如此:'-(
  • JIT 是一种特殊的混乱。 JIT 程序员是一种稀缺资源,因此维护这样的东西会很棘手。 这可能导致 GDScript 发展变得更加困难,因为解释器和 JIT 将紧密耦合。

luaJIT 项目很好地说明了这些要点:该项目简直太棒了(代码库非常好,性能非常好),它解决了一个真实的用例,它被商业公司使用。
然而它正在慢慢消亡,因为它的主要作者已经退后一步,无法跟上新的 lua 版本(luaJIT 停止在 5.1,认为在 5.2 制作的语言中在浮点数之上添加整数将非常复杂add),即使 lua 社区有一个中等规模并且 luaJIT 它最知名的项目。

显然 GDScript 比 Python 甚至 lua 简单一个数量级,但是制作 JIT 的负担仍然很重要,特别是考虑到它不是 Godot 项目的核心功能。

这里唯一可以接受的解决方案是依赖 llvm,它会处理所有疯狂的部分,让我们只向它输出 llvm-ir 代码。 然而,如前所述,llvm 是一个很大的依赖项(此时我们几乎可以使用 Godot 发布 v8 并将 GDScript 写入 Javascript 转译器 :trollface: ),因此在开始之前需要进一步研究以了解需要添加多少任何地方。 再一次,这至少对于 IOS 平台来说不是一个可行的解决方案(该死的苹果!)。

很抱歉在这里听起来如此阴郁,但如果没有提议,我不会发布这个;-)
我猜投票选民对“JIT GDScript”的理解是“GDScript,但与本机代码一样快”。 所以最终实现这个目标的意义(AOT,JIT,转译+编译等)并不重要?

从我看到的 GDScript 来看,一个非常酷(缺乏)的特性是它不是动态语言。 没有exec关键字,没有猴子补丁,调试期间不能修改变量等。
这使得该语言适用于静态编译(事实上,这又是不使用 JIT 的另一个原因)。 最重要的是,新的静态类型系统应该允许轻松优化(与必须进行类型推断相比)。

所以我的想法是从制作一个简单的(没有优化,只是在任何地方使用 Variant)GDScript-to-C 转译器开始,它可以作为godot --gdcc <myfile.gd> 。 使用现有的 GDScript 编译器和 GDNative api 应该会相对容易。
然后让用户处理工具链的其余部分(例如将每个 C 文件编译为 .so 并将原始 .gd 文件替换为指向 .so 的 .gdns 文件)。

第二次,我们可能会开始思考优化问题,更重要的是,将编译器传送到 Godot 的方法。
考虑到第二点,tinycc 似乎是一个非常有趣的解决方案,因为它重量轻,支持的平台数量(x86,amd64,arm,arm64 for linux,win32 和 osx),它libtcc.h api 允许集成它进入一个程序并且它交叉编译模式。 除此之外,0.9.27 版本已于去年 12 月发布(因此该项目似乎还活着^^)。

最后,就像我们目前在导出时将 .gd 文件转换为 .gdc 或 .gde 一样,我们可以创建一个新类型(.gdso ?),它只不过是一个常规的 nativescript(因此 GDNative 资源加载器会在其上调用 Nativescript自动地)。

我同意为 GDScript 编写 JIT 编译器会有点过头了。 正如您所提到的,iOS 上不允许 JIT 编译(Android 确实允许),因此您要么必须回退到该平台上的慢速解释,要么编写 AOT 编译器(看!现在您必须维护两个编译器以及口译员)。

我多次说过,我认为 GDScript2C 转译器是最好的选择,因为我们有了 GDNative。

对于使用 mono 模块构建的二进制文件,您可以编写 GDScript2IL 编译器,然后进行 JIT 和 AOT 编译。

从我看到的 GDScript 来看,一个非常酷(缺乏)的特性是它不是动态语言。 没有exec关键字,没有猴子补丁,调试期间不能修改变量等。
这使得该语言适用于静态编译(事实上,这又是不使用 JIT 的另一个原因)。

实际上,在 GDScript 中,您可以在运行时修改脚本的源代码。

实际上,在 GDScript 中,您可以在运行时修改脚本的源代码。

@neikeq我在写我的帖子之前找了这个,猜我看起来不够近^^
你能给我举个例子吗? (或在代码库中实现的地方)

实际上,在 GDScript 中,您可以在运行时修改脚本的源代码。

不如“修改”。 您可以使用全新的代码设置 Script 的source_code属性,然后调用reload()重新编译它。 从技术上讲,这也是为 C# 实现的,因为它是脚本 API 的一部分。

@touilleMan您的想法类似于#11068 中讨论的内容。

转译 GDScript 或提前编译它的问题在于,即使使用静态类型系统,所有方法调用仍然必须是虚拟的。 任何东西都可能隐藏在Node参数后面。 因此,您会遇到与 Java 类似的性能问题,Java 出于(部分)相同的原因在其字节码上使用 JIT。

无论如何,由于我的硕士论文需要一个实用部分,所以无论如何我都会为 GDScript 实现一个 JIT,所以之后我们可以看到它的表现如何以及它对代码库的破坏程度。 老实说,即使它永远保持原型并且永远不会合并,我也不介意。

此外,对于动态事物,您可以从磁盘上的任何位置加载脚本文件,并将它们用于您可以在项目中使用脚本的任何内容。 对于 DLC 和/或用户创建的内容来说,这是一个非常好的功能(尽管我不知道是否有人实际使用它)。

@vnen C# 中的源代码仅用于编写脚本。 它不是在reload上编译的。

@touilleMan

考虑到第二点,tinycc 似乎是一个非常有趣的解决方案,因为它重量轻,支持的平台数量(x86、amd64、arm、arm64 for linux、win32 和 osx),它允许将其集成到程序中的 libtcc.h api它是交叉编译模式。 除此之外,0.9.27 版本已于去年 12 月发布(因此该项目似乎还活着^^)。

坏主意,这个项目很可能是死的而不是活着的!

@neikeq

对于使用 mono 模块构建的二进制文件,您可以编写 GDScript2IL 编译器,然后进行 JIT 和 AOT 编译。

但这是一个有趣的选择。 并且可能与添加 JIT 的劳动力成本相当。

一般来说,如果反正已经有人要搞这件事,那就让他去做吧。 球队不会因此而输球。 我建议放松等待,突然有事情发生(虽然我不指望它)

@DoctorAlpaca考虑到虚拟方法调度,我猜在使用 C++ API 时事情已经是这样了。 所以你的目标是让 GDScript 比 C++ 更快? ;-)
无论如何,如果我以前的帖子以“没用,你不应该这样做”的方式出现在你面前,我很抱歉。
经验比假设更有价值,所以我只能支持你,除此之外,你的工作很有可能让你改进引擎的各个部分(错误修复、清理,甚至为引擎寻找优化—— dynamic-to-be-fast Variant system)所以什么都不会丢失!
BTW 如果你真的对 JIT 感兴趣,我建议你来 Europython(7 月底在爱丁堡,我会在那里 ^^),Pypy 团队每年都在那里,你可以和他们一起在 Pypy 上进行 sprint 编码在整个周末。 通常人不多,而且他们真的很善良和教学,所以基本上就像和世界级专家一起参加为期两天的 JIT 课程;-)

@ret80我同意 tinycc 项目并不是很活跃。 另一方面,我们谈论的是 C 编译器,最常用的标准仍然是 C99(C89 仍然存在)所以一旦项目足够稳定就没有太多的演变(我想我们可以说演变可以完成在优化中,但这可能会增加编译器的大小和速度,因此不是本项目的目标)。
我对这个编译器最大的担忧是更多地支持 arm 架构和交叉编译 .dll 和 .dynlib,因为它们是更奇特的功能(因此可能测试较少)。
最后,考虑到我们将使用这个编译器和自动生成的代码(总是相同的结构和 gdnative 作为单一依赖),一旦项目走上正轨,我们应该相对安全,不要遇到隐藏的错误。

@neikeq我发现将单声道运行时用于 JIT 的想法非常优雅! 这将保持较低的依赖数量(并提高参与 mono 模块的人数,这总是更好),并且它们将使我们免于制作另一个版本。

老实说,我不会 JIT 而是 AOT 编译。 像 TerraLang(LLVM 的一个 lua 前端,旨在动态构建语言)对于源代码的开发热重载以及编译成二进制本机对象以进行编译链接(无解释,纯 LLVM 编译)将很有用专门针对特定的节点类型将是相当简单的。 虚拟开销可能仍然是一个问题,但它会比现在快得多,尤其是在输入信息开始存在时。 就我个人而言,我只是将 LLVM 嵌入到编辑器/编译器中,是的,它会增加大小,但这也意味着游戏本身最终可能会变得更小,特别是如果你在编辑器中包含目标文件和/或 godot 源并让它将 LTO 全部转化为更小更快的最终输出。

luaJIT 项目很好地说明了这些要点:该项目简直太棒了(代码库非常好,性能非常好),它解决了一个真实的用例,它被商业公司使用。
然而它正在慢慢消亡,因为它的主要作者已经退后一步,无法跟上新的 lua 版本(luaJIT 停止在 5.1,认为在 5.2 制作的语言中在浮点数之上添加整数将非常复杂add),即使 lua 社区有一个中等规模并且 luaJIT 它最知名的项目。

我不确定我是否会称它为死,很久以前它在 github 上被分叉,并且正在更新到现代 LUA 版本等等。

我发现将单声道运行时用于 JIT 的想法非常优雅! 这将保持较低的依赖数量(并提高参与 mono 模块的人数,这总是更好),并且它们将使我们免于制作另一个版本。

尽管如此,仍然不允许在 iOS 等上使用。

我仍然认为 JIT 想法应该以某种方式与 GDNative 合并,以便 GD 脚本被预编译为一种第 3 方 C 操作码,而 GDNative 应该能够解析这个操作码并翻译成 C++ 代码。

@OvermindDL1非常感谢您提到 TerraLang,这似乎是一个非常有趣的项目 ;-)

我不确定我是否会称它为死,很久以前它在 github 上被分叉,并且正在更新到现代 LUA 版本等等。

我并不是很接近 lua 社区,所以我对这个话题的理解可能是错误的。 你能指出我实现新 lua 版本的 luaJIT 的分叉版本吗?

@OvermindDL1非常感谢您提到 TerraLang,这似乎是一个非常有趣的项目 ;-)

他们对公关非常友好,这是一个非常好的项目,但到目前为止还没有被很多人真正注意到。 在内部开发一种语言,内置解析器,LLVM 集成,可以输出完全编译和优化的二进制文件等,这非常好……

即使考虑到这一点,如果我要为 GDScript 编写本机后端编译器,我可能只使用最新版本的 LLVM 的 C++ API。 C API 是稳定的,但 C++ API 的功能要强大得多,实现语言而不只是扫描所需的强大功能,特别是如果您想使用 libclang 来获得完整的优化套件。 然而,TerraLang 也以这种方式完全起作用,并且如果测试较少并使用较旧的 LLVM 版本(C++ API,尽管他们想要更新,所以当然欢迎 PR),使用起来会更加容易。

我并不是很接近 lua 社区,所以我对这个话题的理解可能是错误的。 你能指出我实现新 lua 版本的 luaJIT 的分叉版本吗?

我曾经在https://github.com/LuaJIT/LuaJIT/network上遇到过几个有趣的,它已经被分叉了很多次,并且在几个大的分叉上进行了大量的开发工作。 我在 Lua 社区中也不是很大,我在一些项目中实现了 luajit 以获得脚本支持,但我自己并没有真正编写 lua(显然 lua 现在有一个包管理器,luarocks 或其他东西)。 我遇到过一个或两个大的 luajit 分叉,它也不是直接从 luajit repo 分叉的(所以不会出现在那个列表中),我“认为”我看到的最大的分支没有列在那个列表中。 .

我承认我有点忘记人们在这里提出的所有东西,但似乎至少有人说 GDScript 需要成为静态类型,或者 JIT 应该基于 LLVM。 我强烈不同意这些说法。

最近我开始使用 RPython(PyPy 用来创建 JIT 的工具包)为 GDScript 开发 JIT,并且我也研究过使用 LLVM 作为替代方案。

长话短说:LLVM 不适合 JIT,我已经阅读了相当多的博客文章得出了这个结论,我的经验似乎也证实了这一点。 主要原因有两个:

  1. LLVM 很慢。 我不是指 LLVM 生成的代码,我指的是 LLVM 创建该代码所需的时间会为 JIT 造成瓶颈。 Webkit 的 JIT 从 LLVM 切换到自制解决方案,这也是原因之一

  2. LLVM 假定您的代码是静态类型的,并且不适合编译动态类型的语言。

这就是为什么我放弃了 LLVM(很遗憾,因为我真的很喜欢它),并且正在使用 RPython。 它有优点和缺点:

专业人士

  1. RPython 非常适合:它是一个为动态类型语言构建 JITted 解释器的系统。 除了 Python (PyPy),人们还编写了 PHP 解释器,结果证明它们比官方语言、Ruby 和自定义语言快得多。

  2. RPython 是一个为动态类型语言构建跟踪 JIT 的出色系统。 对于动态类型语言,跟踪 JIT 似乎是最好的解决方案。 实现跟踪 JIT 是一项艰巨的工作,需要在非 jitted 代码中添加测量点。 RPython 现在已经存在了 10 多年,有非常聪明的人在研究它(并且已经发布了一些关于 RPython 的学术论文),并且它只需要你做很少的事情就可以运行 JIT。 基本上,它希望您在 RPython 中编写一个字节码解释器,并告诉它哪些字节码是跳转指令,然后它会完成其余的工作(并且做得也很好;早期的实验表明,对于计算繁重的代码可能会加速 40 倍)

  3. JITted 代码非常快,它支持 x86、x64 和 ARM 后端

  4. 这是一个活跃的项目,有很多人在做这个项目,并且是一个活跃的社区。

  5. 字节码解释器和函数非常易读。 Variant_op.cpp 中的当前实现看起来很糟糕; 不要误会我的意思,我知道为什么它看起来是这样,并且它对于良好的性能是必要的,但是破解它并不是很有趣。

缺点

  1. (部分)Variant.cpp 需要在 RPython 中重写/复制。

GDScript 的动态类型解决方案 Variant 类使得创建良好的 JITTed 代码变得困难。 例如,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-40m 才能完成一些完整的实施。 然而,这并不像看起来那么可怕,因为只有在修改 JIT 本身时才需要生成 C 代码,否则 Godot 编译过程可以只使用生成的 C 文件。

我希望能在 4-8 周内完成 PoC,以获得更多反馈。

@baekdahl阅读过多。 不管你怎么想,GDScript 都会变成类型化的。 该决定来自最高层,并将在不久的将来发生。

@CarlGustavAlbertDwarfsteinYung GDScript 正在成为可选类型。 这个问题差别很大。

长话短说:LLVM 不适合 JIT,我已经阅读了相当多的博客文章得出了这个结论,我的经验似乎也证实了这一点。 主要原因有两个:

那是因为 JIT 应该在开发时。 在发布时,应该进行完整的静态本机编译。 LLVM 可能不是最快的 JIT,但它一种 JIT,同时也能够完全本地编译为独立的库/对象/程序。

LLVM 假定您的代码是静态类型的,并且不适合编译动态类型的语言。

嗯,实际上它有一些非常棒的动态类型接口。

无论如何,如果 GDScript 正在变成类型,那么无论如何这都不是问题,GDScript 的“动态”部分无论如何都会变成一个变体。

这就是为什么我放弃了 LLVM(很遗憾,因为我真的很喜欢它),并且正在使用 RPython。 它有优点和缺点:

@brakhane它能够进行本机编译吗? 如果不是,那么它似乎不是一个初学者,因为你不能在 iOS 等平台上进行 JIT,此外,运行时的 JIT 会减慢加载和假脱机,而 AOT 在发布时没有这样的成本。

GDScript 正在成为可选类型。 这个问题差别很大。

是的,但即使是“无类型”部分也是变体,这在 LLVM 中表示相当简单。

那是因为 JIT 应该在开发时。

那么就不是JIT,而是正常的提前编译。

它能够进行本机编译吗? 如果不是,那么它似乎不是一个初学者,因为你不能在 iOS 等平台上进行 JIT,此外,运行时的 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,因为如果单独编译各个模块,那么无论如何它都不是什么速度问题(<1s 容易)。

不,这是 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的类型

我很想听听这些。

除了 LLVM 本身具有的内联本机代码生成 JIT(这将与上述所有内容完美配合,并且对于开发时编辑代码运行时仍然能够为发布执行 AOT 非常棒),它允许直接JIT 翻译,还有一些库,如 pyston、fastlua 以及其他基于 LLVM 构建的库,它们具有各种形式的跟踪 JIT 以按需改进,我个人认为这对开发时间毫无用处,而且你不能在可靠的版本中使用无论如何,因为像iOS这样的平台,所以我不明白这一点。

@OvermindDL1您仍在谈论在保存已编辑的 GDScript 时自动触发的 AOT。

是的,如果 GDScript 获得可选的静态类型,那些强制类型的部分可以转换为非常有效的机器代码。 GDScript 的功能也很有限,所以我认为即使在任何地方都严格静态化也不会有太大的损失。

如果人们想实现这样的事情,他们可以。 我已经编写了一些静态编译器,这个问题对我来说并不有趣。

我感兴趣的是为在 PC 上运行的动态类型 GDScript 创建 JIT。 如果其他人最终也会使用它会很好,但这对我来说不是必需的。 不过,我很欣赏“您可能在浪费时间”的警告。

嗯,这甚至​​不是我建议诚实做的方式。 通过访问者执行本机 < 操作将比仅作为一个示例的字符串测试具有更高的性能。

IIRC,它不是真正的“<”,而是给出的更多 Operation.LT 参数。

不过,这似乎很容易优化?

但不是,除非您想重新实现一个全新的 GDScript 解释器。 即便如此,除了琐碎的情况外,您也很难推断出类型。

如果您只是想替换操作码解释器部分,这并不是一件容易的事(有趣的事实:“.gdc”文件不是字节码,而更像是被解释并稍后转换为内存字节码的标记化源代码)。 此时,您知道以下内容:

  • 有一个变量 i,它与另一个内容不透明的变量进行比较(“小于”)(尽管您可以使用内部 API 来查看它是一个值为 10 的整数)
  • 还有另一个变量“foo”,它以 i 作为参数调用
  • i 被分配了一个新值,这是将 i 添加到另一个不透明变体的结果

如果你想能够做 AOT 编译,你基本上必须重新实现整个 GDScript 解释器。 这并不像听起来那么简单,因为解释器的重要部分是 Variant 类的一部分。 Variant 将 godot 结合在一起,即使不是 GDScript 的部分也严重依赖 Variant。 因此,当您开始弄乱它时,您突然冒着破坏 GDNative 接口的向后兼容性的风险。

这确实不像一开始听起来那么容易。 当我第一次潜入它时,我也很惊讶。

您仍在谈论在保存已编辑的 GDScript 时自动触发的 AOT。

并非无处不在,而仅在出口上。 现在我希望它在保存时是 AOT,但不是必须的,你可以在通过编辑器运行它时只在 Release/Export 和 JIT 上进行 AOT,以允许在运行时热交换代码。

是的,如果 GDScript 获得可选的静态类型,那些强制类型的部分可以转换为非常有效的机器代码。 GDScript 的功能也很有限,所以我认为即使在任何地方都严格静态化也不会有太大的损失。

根据当前的规范,有很多推断,“大多数”代码通常应该通过推断自动键入。 我对此感到非常兴奋。 (我是 HM 打字的粉丝,多年来以各种方式实现了十几次!)

我感兴趣的是为在 PC 上运行的动态类型 GDScript 创建 JIT。 如果其他人最终也会使用它会很好,但这对我来说不是必需的。 不过,我很欣赏“您可能在浪费时间”的警告。

是的,JIT 绝对不应该是强制性的,因为这意味着它不能导出到所有支持的平台(除了增加开销)。 AOT 或解释应该始终是主要方法,并且考虑到大多数不支持 JIT 的平台(iOS、WebAssembly 等)也往往是效率最低的平台,那么 AOT 绝对应该是默认的,而不是解释,因为正在添加打字,这甚至更好。

但不是,除非您想重新实现一个全新的 GDScript 解释器。 即便如此,除了琐碎的情况外,您也很难推断出类型。

我不是在谈论解释,我在谈论编译。 如果编译了完全不可键入的代码,那么它只会变成机器代码的变体(标记的 sum 类型),并且对它的任何访问,例如operator<函数只会对其执行 vistor 调用,这涉及单个基于动态类型值的静态跳转,然后执行静态代码(如果是内联整数,如果是自定义类型,则可能最终会执行虚拟调用,甚至如果它是直接在变体中实现的大型类型(如向量),则为静态调用)。

如果您只是想替换操作码解释器部分,这并不是一件容易的事(有趣的事实:“.gdc”文件不是字节码,而更像是被解释并稍后转换为内存字节码的标记化源代码)。

一点也不,操作码在 AOT 完成时不应该存在,它应该是纯机器码,降低到机器码上最大的变体调度或最小的完全已知和类型化的高效机器码。

有一个变量 i,它与另一个内容不透明的变量进行比较(“小于”)(尽管您可以使用内部 API 来查看它是一个值为 10 的整数)

如果变量i的类型是已知的,那么您可以生成机器代码来测试不透明变体的类型,如果它不是立即兼容的匹配则立即出错,否则然后根据i执行比较i的类型未知,那么它将是对两个变体(静态已知函数调用)的完整访问者调度,以获取变体的类型标签并跳转到适当的机器代码给定的类型。

还有另一个变量“foo”,它以 i 作为参数调用

如果foo的调用类型是已知的,例如 ML 符号中的int -> int ,那么如果i的类型也是已知的并且也是一个 int 则执行高效机器码,如果i的类型未知,则测试类型标记是否匹配,如果不匹配则错误,如果匹配则提取值并执行有效的机器码。 如果foo的调用类型未知,但类型标记表明它是可调用的(否则错误),那么如果i的类型已知(对于known所有定义在这里我的意思是在编译时都知道并且因此被拆箱)然后将它包装在一个变体中(正确类型标记的有效内联设置并将i的位放入正确的位)并执行变体调用到包装的蹦床函数,然后验证参数并调用实际的内部函数(如果foo的调用类型是静态已知的,则直接调用该函数)。

i 被分配了一个新值,这是将 i 添加到另一个不透明变体的结果

一切都依赖于i现在的已知静态类型,并根据需要执行适当的上述本机代码生成。

如果你想能够做 AOT 编译,你基本上必须重新实现整个 GDScript 解释器。

这仍然比专门为 GDScript 创建 JIT 工作要少,除非您包装现有的 JIT(其中预期的操作可能与 GDScript 不完全匹配)。 AOT 生成本身并不比创建解释器困难多少,尤其是在 LLVM 上,因为它为您完成了非常艰巨的工作(优化、LTO、机器代码生成、调试器等......等等......)。

这并不像听起来那么简单,因为解释器的重要部分是 Variant 类的一部分。

但仍然没有暗示的那么难(几十年来我写过各种各样的语言,很多都是机器代码,直接到 x86 和通过 LLVM)。

Variant 将 godot 结合在一起,即使不是 GDScript 的部分也严重依赖 Variant。 因此,当您开始弄乱它时,您突然冒着破坏 GDNative 接口的向后兼容性的风险。

这正是为什么应该将变体直接烘焙到机器代码中的原因。 没有操作码解释器或任何类似的东西,而是进行完整和适当的访问者调度(无论是通过基于类型标签的本地跳转,还是通过来自静态数组(调度表)的长调用)。 这不仅可以保持向后兼容性,而且还允许 GDNative 接口直接加载完全 AOT 编译的库(或者如果 Godot 能够通过二进制对象将引擎 LTO 到 GDNative 接口,则可以应用 LTO 优化,这可以然后显着减小最终程序的输出大小,因为可以删除已知未使用的代码,尽管这将是以后的任务)。

这确实不像一开始听起来那么容易。 当我第一次潜入它时,我也很惊讶。

这当然是一项不平凡的工作,事实上,其中大部分是大量繁忙的工作来翻译以前的 GDScript 编译管道以生成 LLVM 汇编程序代码(尽管它有一个很棒的 AP​​I 可以这样做)而不是操作码。 但这是完全可行的,而且在给定语言实现的情况下,时间并不像人们预期的那么长。

顺便说一句,所有变体运算符都在这里处理: https :

这已经相当优化了。

顺便说一句,所有 Variant 运算符都在这里处理:

啊很酷,所以它已经在使用计算机 goto,然后可以使用它(尽管可能也想将它扔到 LLVM 中,以便它可以在可能的情况下内联已知的调用路径,但是是的,就像这样,这非常一个变体访问者,并且将在根本不知道类型的最坏情况下使用。虽然如果对类型有任何了解,那么进行更直接优化的调用会更快,当然如果所有类型都已知或只有一个有效分支,然后可以生成优化的非变体机器代码(通过从变体中提取数据(更有可能在转换位部分中传递)或仅使用寄存器/堆栈/堆中已经存在的数据(如果是本地的)或已提取)。

但正如我所提到的,你并没有从中获得太多的速度提升。

像这样编译 GDScript 相当容易:

当 i<10 时:
富(一)
我 = 我 + 1

相当于下面的 C++

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

(或类似的东西,不记得确切的 Variant API,已经有一段时间了)

但这只会让您的速度提高 2 倍左右。

那么在大多数情况下,它不是已经使编译的 GDScript 比 C# 更快了吗?
使用更简单的语言获得更好的性能对我来说听起来很划算。

那么在大多数情况下,它不是已经使编译的 GDScript 比 C# 更快了吗?

可能不会,因为它必须通过整个 Variant 间接层。 查看诸如 Wren 之类的语言,似乎使用 NaN 标记或未装箱类型比将事物编译为机器代码更不利于性能。 C# 具有未装箱类型_和_ JIT 编译,因此将 GDScript 编译为机器代码不会神奇地使其比 C# 更快。
请注意,GDScript 已经有一个非常优化的字节码分派器,让机器码来做同样的事情不会加速瓶颈。

好吧,也许一个好的解决方案是从 gdscript(也可能是 sconscript 文件)生成未优化的 c++ 代码,然后用户会优化(这应该不是什么大问题,因为用户编写了原始脚本)并编译它手动 .

它并不完美,但它已经比手动完成所有事情要快。

如果要编写两次代码,为什么不直接用 C++ 编写呢?

这是将 GDScript 直接翻译为 LLVM 的另一件事,可以 AOT 任何平台的二进制文件,而不仅仅是您当前所在的平台和可交叉编译的平台。

这个问题让我想起了Boo语言。 一种在 .NET 上运行的受 Python 启发的语言。

我不知道它的价值(我还没有真正使用过它),而且这个项目似乎已经死了(5 年?该死的......)但我还是把它扔了。

我更喜欢 Kotlin 的类型声明。 它更具可读性

var name:String = "Bob"

@Logmytech您迟到了半年,Typed GDScript 已经是master(并且恰好使用了Kotlin-ish 类型声明)。

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 中改进性能。

我在使用 GDNative 将 GDScript 编译为 C 方面做了大量工作。 我已经到了一个我不相信这是值得努力完成的地步。 从理论上讲,AOT 代码的执行速度可能比 GDScript 快几个数量级,但实际上结果更令人失望。 通过 GDNative 层的成本很高,如果不对 GDScript 本身进行重大更改,编译后的代码非常脆弱(GDScript 字节码不是以在以后执行时安全运行的方式生成的,因为它期望环境是静态的,包括引擎内部数组中事物的位置)。

然而,我相信,通过实施最少量的后端优化,可以实现一些真正的性能改进。

我正在开发一个包含许多优化的分支,这些优化根本不需要对 GDScript 进行重大更改,但已经获得了一些性能提升。

到目前为止,我已经实现了样板数据结构来执行代码优化,以及一些基本的优化过程:

  • 控制流图的构造和销毁
  • 数据流分析,包括 gens 和 kill set、live range
  • 死代码消除
  • 跳转线程(重要优化),因为 GDScript 创建了许多不需要的分支

作为 POC,我计划在推叉之前再实施几次:

  • 公共子表达式消除
  • 临时对象的类型推断和临时对象的本机算术(我也不相信类型算术会是一个巨大的胜利)

类型化算术运算确实需要修改 GDScriptFunction 和函数状态类以包含类型化寄存器数组,以及用于寻址类型化寄存器并避免在运行时进行额外类型检查的新操作码。

您可以在我的 godot fork 中看到我在上述优化方面的进展, https://github.com/pchasco/godot/tree/gdscript-optimization/modules/gdscript/optimizer

@pchasco你可能想和@vnen 谈谈,他目前正在为 Godot 4.0 重新编写 GDScript - IMO,这将是确保获得如此低调的果实的好时机:)

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

@pchasco你可能想和@vnen 谈谈,他目前正在为 Godot 4.0 重新编写 GDScript - IMO,这将是确保获得如此低调的果实的好时机:)

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

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

好吧,VM 将更改为集成类型指令,所以我不确定现在进行优化工作是否是最好的。 我确实相信这些想法可以在新的 VM 中使用,但它需要一段时间才能完成。

不过,我不确定我们是否会拥有 JIT,所以也许这个问题可以被关闭以支持其他东西。 有一个做 AOT 编译的想法,它比这更容易管理。

@vnen是的,问题标题不再合适,如果您阅读以上内容,他们发现 JIT 主要不值得,而是在当前解释器中进行有针对性的优化。

在重做时记住这些可能是值得的,但是,是的,当一个好时机看到这些时,你会比我更了解:) 只是想把你们两个联系起来

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

@pchasco你可能想和@vnen 谈谈,他目前正在为 Godot 4.0 重新编写 GDScript - IMO,这将是确保获得如此低调的果实的好时机:)
https://godotengine.org/article/gdscript-progress-report-writing-tokenizer

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

我看到你找到了我的博客!

好吧,VM 将更改为集成类型指令,所以我不确定现在进行优化工作是否是最好的。 我确实相信这些想法可以在新的 VM 中使用,但它需要一段时间才能完成。

不过,我不确定我们是否会拥有 JIT,所以也许这个问题可以被关闭以支持其他东西。 有一个做 AOT 编译的想法,它比这更容易管理。

我同意 JIT 不是一个可行的解决方案,考虑到平台的数量、不同的硬件以及维护所需的工作量。 AOT 绝对是使用 GDScript 实现本机性能的最简单和最便携的解决方案。 事实上,Unity 也为它的 AOT 将 .NET 转换为 C++。 我有兴趣为 GDScript 重写做出贡献。

@pchasco我建议加入#godotengine-devel IRC 频道并在那里取得联系:)

正如我之前提到的,我们不太可能添加 JIT。 讨论中还有一些其他的想法,但没有结论。

由于我们正在将提案移至 GIP 跟踪器,因此我将关闭此提案,如果有人有想法,请打开一个新提案(在查看是否没有其他人先做之后)。 这个问题可以在相关提案中链接,以保留讨论历史。

一些贡献者对 AOT 编译有不同的想法,但我会让他们根据他们的想法打开一个提案。

@vnen
那么请更新路线图,它会误导人们。

好吧,我什至忘记了路线图的存在,它很长时间没有更新。

@vnen
你忘了,但其他用户没有。 所以请更新它,因为很多人都牢记在心。

解释器是垃圾,对我来说,当性能糟糕透顶时,这会扼杀 gd 脚本的用处

在编写基本模拟器时使用解释器,而不是完全成熟的游戏引擎

一个及时的编译器通常可能比一个缓慢的、过时的编译器快,解释器系统简单明了,我觉得我宁愿撕掉 gd 脚本,只用 c++,然后做一个带有准时系统的隔都设置

我认为人们不愿意承认解释器是垃圾,因为人们出于某种原因不想伤害彼此的感情,但是与 c++ 相比,最终结果是漂亮的,ehhh ...

它是....好吧,如果你有一些非常基本的事情我猜,但是,对于任何严肃的事情,它只是,就像它不适合我脑海中的大部分事情,除了非常简单的游戏

@RaTcHeT302无需带入您的消极情绪,并记住我们有行为准则。 有计划以多种方式优化 GDScript,包括编译为本机代码,但不使用 JIT。 此外,如果您与 C++ 进行比较,即使是 JITed 运行时也会很慢。

@vnen :只是说 LuaJIT 在性能基准上几乎与 C/C++ 一样好——但人们普遍认为这涉及到魔法:微笑:

并非所有主要平台都支持 JIT。 最好集中精力
所有人都可以利用它们。 提前编译是唯一的
100% 兼容技术。

2020 年 8 月 9 日星期日上午 8:46 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
.

与 JIT 相比,AoT 没有缺点吗? 例如,动态生成或加载的脚本呢? JIT 优化它们应该没有问题,但是 AoT 呢?

顺便问一下,在哪些主要平台上是不能 JIT 的? 我认为带有 JIT 的 Java 几乎可以在任何地方运行。

编辑:哦,我猜苹果是问题所在,禁止使用 JIT。 所以这是一个任意的供应商锁定限制,而不是技术限制。 很高兴我不必支持任何 Apple 设备...

iOS 不允许通过 App Store 分发的应用程序进行 JIT
除非它使用 Apple 的浏览器组件。

对于动态脚本,AOT 是不可能的。

2020 年 8 月 9 日星期日上午 11:15 monnef [email protected]写道:

与 JIT 相比,AoT 没有缺点吗? 例如关于
动态生成或加载的脚本? JIT应该没有问题
优化它们,但是 AoT 呢?

顺便问一下,在哪些主要平台上是不能 JIT 的? 我以为Java
它的 JIT 几乎无处不在。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/godotengine/godot/issues/5049#issuecomment-671064117
或退订
https://github.com/notifications/unsubscribe-auth/AAUFIACWWCWDXO4NZ7GCL63R724RHANCNFSM4CFZMZPA
.

iOS、Web 和少数控制台平台不支持 JIT 编译。

有传言说 AOT 现在被 JIT 淘汰了。 我希望,那不是真的。 有人请告诉我,这是真的吗?

你是说有传言称某些地方将不再允许使用 AOT
设备? 这实际上是不可能的,除非我误解了什么?

2020 年 9 月 30 日星期三上午 5:47,Teashrock通知@github.com 写道:

>
>

@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。

也许它不会得到官方支持,但我可以确认新的
vnen 概述的 GDScript 架构能够支持它。 一个
有进取心的开发人员可以实现一个自定义模块来做 AOT
GDNative C.

2020 年 9 月 30 日星期三上午 11:16,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 是否会在 4.0 中使用 JIT 或 AOT 编译,但不太可能。 也许在 4.1 或更高版本中......

如果您需要更高的性能来处理数字(即您的 CPU 限制显然来自脚本语言),请使用 C#。 但首先,请阅读优化教程:slightly_smiling_face:

@Calinou
只是他们谈到了关于 JIT/AOT 的 4.0。 现在你说我是 4.1。 为什么? 怎么样? 有人,只要给每个人一个明确的公共 anwser,它不会在 GitHub 上的某个地方。

先感谢您。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

Spooner picture Spooner  ·  3评论

timoschwarzer picture timoschwarzer  ·  3评论

testman42 picture testman42  ·  3评论

bojidar-bg picture bojidar-bg  ·  3评论

ivanskodje picture ivanskodje  ·  3评论