3

我想允许我的应用程序被“脚本化”。脚本语言应该是类型化的和类似 C 的。它应该具有在 C 和 Java 中都可以找到的常用控制语句、原始类型、数组和运算符。脚本应该能够定义函数,并调用预定义的 API 函数,例如我选择提供的 sin() ...。如果脚本通过了语法检查,那么它将被应用程序翻译成 Java,然后即时编译,但也应该可以将其翻译成 C/C++,并进行本地编译。Java/C 的语法检查和翻译应该在 JVM 中运行。我不需要解释器,因为它总是会被翻译成 Java/C。只有一个语法检查器和翻译器。

有这样的语言吗?如果不是,考虑到我不熟悉编译器/解释器编程,在 JVM 中执行此操作的最简单方法是什么?(如果我是,我不需要问这个问题......)

如果有一个“Scala”解决方案,那也很好,因为我实际上将我的 Java 代码移动到了 Scala。

[编辑] 我想要 C/C++ 翻译的唯一原因是性能。我期望对数组进行很多“位操作”,而 Java 并不真正适合,特别是由于在每个数组索引操作时都会进行范围检查。我还期望有很多对象,这会在 GC 周期中间接消耗。

[EDIT#2] 好的,我明白我必须得到具体的。我正在考虑对 Minecraft 克隆进行编程,作为练习,让我的注意力从“商业计算”中解脱出来。我说的是引擎,而不是游戏玩法。而且我对服务器端比对 3D 更感兴趣,因为我是一个“服务器人”。我们正在讨论使用飞行重量模式来表示数百万个对象(块/体素),并每秒多次访问它们。这不是 JVM 的用途。请看这篇文章,了解我的意思:为什么我们选择 CPP 而不是 Java

我不想用 C/C++ 编写所有东西,但我怀疑这是获得良好性能的唯一方法。我的意思的另一个例子是VoltDB,它可以说是目前最快的 SQL 数据库。他们用 Java 和 C/C++ 编写它,使用 Java 进行 I/O 和网络,使用 C 进行繁重的内存操作和有点摸索。用户编写的存储过程是用 Java 编写的,但我认为不需要。我认为应该可以在客户端和测试版本中编译为 Java,并在可以配置完整开发环境的服务器上编译为 C。

4

9 回答 9

4

也许Haxe会满足您的需求。它是一种可以编译成 C++ 源代码的中级高级语言。Java 目标正在开发中。

于 2011-07-07T11:12:04.983 回答
3

两个字:过早优化。

你关心性能。但是考虑到你想制作一个 Minecraft 克隆,这意味着游戏世界可以很好地用一个 3D 数组来表示。在所有提到的编程语言中访问这些都相当快;游戏逻辑的执行时间应该比访问数百万个数组条目要多得多。那么,为什么要优化一个无论如何都不会占用大部分计算时间的部分——甚至在你编写一个最低限度的工作版本之前呢?

您可能想要创建一个代表游戏世界的 Java 接口或 Scala 特征。它提供了获取和存储游戏世界块内容的方法。稍后,您还可以添加批量方法以进一步优化性能;例如,将检查给定立方体中的所有块是否都是空的,或者计算木块的数量,沿着这些线。但一开始,最好忽略那些方法,或者做一些依赖重复调用抽象方法的琐碎实现。您可以稍后对其进行优化。

然后,您可以提供该接口的一个非常简单的 Java/Scala 实现,它实际上使用了一个三维数组。另一种方法是映射其键是坐标,值是块状态。优点是游戏世界的大小没有真正的限制,空块不会占用任何内存(对于空块的坐标,地图中没有条目)。缺点显然是性能。

此时,如果数据消耗过多内存,您可能需要考虑更紧密地打包数据。您可以使用位集。当您到达那个阶段时,使用 JNI 将一些用 C 或 C++ 编写的代码注入 JVM 中实际上是有意义的。因此,您将游戏逻辑保留在 Java/Scala 中,并在 C 中进行内存打包和查找。

创建一个可以创建 Java/Scala 和 C/C++ 版本的代码本机部分的通用“脚本”源没有真正意义;本机 C/C++ 函数将严重依赖无法直接转换为 Java 的优化。当您想以“纯 Java/Scala 模式”启动服务器时,即不使用 JNI 函数,只需使用您在上一步中创建的其他类。它们可能会慢一点,但它们是纯 JVM 字节码。而且由于您使它们保持简单,因此您不必疯狂地扩展它们或向它们引入新的错误。至少,创建或调整跨编程语言代码生成器的开销远远大于保留两个单独的代码库,尤其是在 Java/Scala 实现非常简单的情况下。

当然,位包装只能让你走这么远。您可能想注意到游戏世界的某些部分几乎完全是空的(尤其是地表以上的部分),而其他部分则包含充满相同类型块的巨大区域(例如几乎完全由石头组成的地下区域)。维护一个具有这么多冗余的巨大内存结构确实是在浪费内存。因此,您可能会考虑将游戏世界打包成一棵树,其中每个节点代表游戏世界的一个大立方体区域,孩子们将其进一步细分,直至描述仅描述一个特定游戏世界坐标内容的叶子。当一个节点只有相同内容类型的子节点时,您不需要存储子节点。在这一点上简单地砍树并让节点说,“你不需要再看下去了。很好!- 当然,你必须保持树是可变的。如果世界中仅由一个节点表示的无聊部分发生了变化,您需要打开该节点并将其拆分为两个或多个子节点。如果之后又变得简单,你可以再次和孩子们一起砍树。

此时您可能会注意到的一件事是:内存打包和访问优化在这里不再是真正的问题。像这样的树不能通过使用存储和查找方法的本机函数来合理优化。如果你能从这样的优化中获得超过 10% 的收益,那么这将是非常不可能的并且非常令人印象深刻。(更可能的是,这可能意味着 Java/Scala 对应的优化很差。)如此最小的速度提升并不能证明需要投入的巨大额外努力是合理的。而是将更好的 CPU 放入机器中,享受通过吃冰淇淋、观看 Dr. House 或继续进一步增强游戏并使其对玩家更有趣和更具吸引力所节省的时间。通过创造能够真正改善产品的有价值的东西。

但这仍然不是它。如果我没记错的话,Minecraft 世界的初始状态是程序生成的。使用分形算法,您真的可以在眨眼间创造出让人感觉复杂自然的无尽区域。因此,与其预先计算游戏世界的内容并将其存储在一个巨大的数据结构中,不如使用世界生成过程作为查找方法:与其从内存中查找坐标的内容,不如简单地使用算法。这样一来,世界的初始状态就可以完全存储在四个字节中:算法的种子值。

当然,世界不会一直停留在这种状态。当玩家(或其他东西)改变世界时,这就是你需要存储的东西。因此,您只存储世界的种子值和对其所做的更改。每当您查找坐标的内容时,请尝试在更改的图块存储中找到它。当它在那里时,使用该信息。当它不存在时,默认为程序世界生成算法。这将使您的内存消耗大大减少。而且由于世界的变化相对较小并且包含巨大的空白区域,因此您应该相对容易编写一个快速有效地存储这些变化的数据结构。同样,为此编写本机代码不会产生显着的性能提升,也不值得付出努力。

但是可以优化其他东西:程序世界生成算法。这是您可能希望用 C 或 C++ 编写的一个关键组件。它应该相对较小且代码不多,但它是数学密集型的,并且会经常被调用。所以优化它并用它制作一个小型 JNI 库。这将为您带来巨大的性能提升,值得付出努力。(当然,你可能想先做一个 Java/Scala 实现。如果这已经足够快,那么就没有必要陷入 JNI 的麻烦。)

如果你的世界生成过程仍然太慢,那么你可以为它实现一个缓存。缓存甚至可以在JVM有一些懒惰的时候抢先生成玩家的一些环境。

我为您列出了这个开发过程,作为几个想法的迭代,一个比前一个更好。您可能已经在第一阶段就开始编写优化的 C/C++ 代码库了。这将是浪费时间;你本可以在后期把它全部扔掉。使用 C 语言编写的位打包的高效数组存储是一件好事,但是当您将世界重新组织成二进制空间分区树时,它绝对没有用。

所以,不要过度。当您无法仅在 Java/Scala 中创建最低限度的工作(但缓慢且未优化)版本时,您也无法在 C/C++ 或某些交叉编译脚本语言中创建优化版本。先做简单版本,再做性能测试,真正需要的时候才优化。不要通过首先制定优化概念来开始您的项目。这种优化应该是您最后要做的事情。

于 2011-07-08T01:52:27.373 回答
2

有这样的语言吗?...在 JVM 中执行此操作的最简单方法是什么,

我会使用纯 Java 并让 JVM 将代码编译为本机机器码。如果需要,可以使此编译更具侵略性。

也许您可以澄清您希望获得的 JVM 尚未提供给您的东西。


如果你真的想开发自己的迷你语言,你想对它需要什么有一个现实的想法。如果你不做出承诺,你很可能会想出一些不太好的东西。

http://www.ohloh.net/p/openjdk/estimated_cost

OpenJDK: Project Cost Calculator

Include     
Codebase    4,782,885 lines
Effort (est.)   1451 person-years
Estimated Cost  $ 79,805,125
于 2011-07-07T11:04:30.283 回答
2

JavaScript 语法类似于 C/C++ 和 Java。存在各种 JavaScript 引擎,其中一种用 Java 编码的是Rhino

还有LLVM,它是一个编译器引擎,可以将代码编译成自己的字节码,然后再编译成机器码。它还集成了 JIT,并且存在许多语言前端。我对这个项目了解不多,但看起来很有趣。

于 2011-07-07T11:19:33.333 回答
1

由于 Scala 似乎是一种选择,所以坚持下去。

您可以直接从您自己的代码调用解释器(由 REPL 使用),它将脚本编译为 Java 字节码,然后运行。您将很难找到在功能和灵活性方面与此相匹配的解决方案 - 特别是考虑到静态类型的要求。

至于性能,JVM 负责将字节码进一步编译为本机代码,它也很擅长这项工作。我怀疑你会看到 C/C++ 的任何显着性能提升(特别是编译时间会更糟)。

于 2011-07-07T13:34:36.660 回答
0

这是完整的 C,但可能对您的目的有用。西比尔

于 2011-07-07T11:08:53.870 回答
0

我发现 Groovy 在 JVM 上作为类 C 脚本语言工作得非常好。

不确定你能把它翻译成 C/C++ 的效果如何,但是如果你有 C/C++ 代码需要调用,那么你可以很容易地用 JNI 链接到它。

我认为将基于 JVM 的代码转换为 C/C++ 没有其他价值。对于明显的编译路径(JVM 语言 -> JVM 字节码 -> 本机),JVM JIT 已经是一个非常好的编译器,并且几乎肯定会击败任何试图做更复杂的事情(JVM 语言 -> C/C++)的性能-> 本机)。

于 2011-07-07T11:12:49.803 回答
0

JavaScript 呢?我没有满足你的所有要求(它只是弱类型),但它似乎满足了其中的不少:类 C、通常的控制语句,以及运算符、数组、函数,你可以提供自己的 API...它甚至可以编译成 Java,但现代解释器无论如何都非常擅长即时编译它。如果您使用 Java 编写,我强烈建议您看看Rhino

于 2011-07-07T11:29:03.977 回答
0

在阅读了所有答案后,这就是我决定做的事情。为了在我的项目中抢占先机,我不会在一开始就使用脚本。我要做的是用 Java 编写程序,但不使用任何有问题的结构。

首先,我将创建一个API基类,其中包含我可能需要的任何实用方法。95% 的代码只会委托给一些为我工作的库。这个 API 类将具有原始集合类的工厂方法。这也将是一些实用程序类,如 Point、Event、...

然后,我将通过扩展 API 对象来创建我的游戏代码。我不会使用访问修饰符,所以一切都是包公开的。我不会使用“final”或“static”或“this”或“super”或“abstract”或“interface”或数组或通用泛型或类转换或“instanceof”或“new”或异常处理。我不会导入任何东西或访问那个标准的 Java API,除了 String 类。我需要从标准 Java API 获得的任何东西都必须包含在我的 API 基类中。运算符也有一些限制,特别是按位限制。

为了克服由于不使用泛型或类转换或“instanceof”而造成的限制,我将使用控制反转、依赖注入和一些反射来解决问题。

这应该相对容易和快速编程,但不会执行。稍后,当我的引擎稳定并被调试后,我可以回到我最初的想法,并创建一个解析器,它将转换为优化的 Java 或 C++。删除所有这些特殊的Java 结构将使构建解析器变得更加容易,并且由于我将使用标准 Java 语法,我可以只使用预定义的解析器,并根据我的需要对其进行自定义。

于 2011-07-09T09:35:08.490 回答