是否有类似C的迷你语法/语言可以翻译为本机C / C ++和Java?

问题描述 投票:3回答:9

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

那边有这样的语言吗?如果没有,考虑到我对编译器/解释器编程知之甚少,在JVM中这样做最简单的方法是什么? (如果是的话,我不需要问这个问题......)

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

[编辑]我想要C / C ++翻译的唯一原因是性能。我期望对数组进行大量“位操作”,而Java并不适合这种操作,特别是由于每个数组索引操作的范围检查。我还期望许多对象,这些对象在GC循环中间接成本。

[编辑#2]好的,我知道我必须具体化。我正在考虑编写一个Minecraft克隆,作为练习,让我的思绪脱离“商业计算”。我说的是引擎,而不是游戏玩法。而且我对服务器端比对3D更感兴趣,因为我是“服务器人”。我们谈论使用飞行模式来表示数百万个对象(块/体素),并且每秒访问它们多次。这不是JVM的用途。看看这篇文章我的意思是:Why we chose CPP over Java

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

java c scala code-translation
9个回答
3
投票

两个词:过早优化。

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

您可能想要创建代表游戏世界的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放入机器中,享受通过吃冰淇淋节省时间,观看House博士或继续进一步提升游戏性能,让玩家更有趣,更有吸引力。通过创造有价值的东西来真正改善产品。

但这还不是。如果我没记错的话,Minecraft世界的初始状态是程序生成的。使用分形算法,您可以真正创建无限的领域,在眨眼间感觉复杂和自然。因此,您可能希望使用世界生成过程作为查找方法,而不是预先计算游戏世界的内容并将其存储在大型数据结构中:而不是从内存中查找坐标的内容,只需使用算法。通过这种方式,世界的初始状态可以完全存储在四个字节中:算法的种子值。

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

其他东西可以优化:程序世界生成算法。这是您可能希望用C或C ++编写的一个关键组件。它应该相对较小而且代码不多,但它是数学密集型的,并且会经常调用。因此,优化它并从中创建一个小型JNI库。这将为您带来巨大的性能提升,值得付出努力。 (当然,您可能希望首先执行Java / Scala实现。如果已经足够快,则无需进入JNI问题。)

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

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

所以,不要过分。如果您无法单独在Java / Scala中创建最低工作(但速度慢且未经优化)的版本,则无法在C / C ++或某些交叉编译脚本语言中创建优化版本。先做简单版本,然后进行性能测试,只在有实际需要时进行优化。不要先通过制作优化概念来启动项目。这种优化应该是您将要做的最后一件事。


4
投票

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


2
投票

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

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


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

1
投票

由于Scala似乎是一种选择,只需坚持下去即可。

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

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


0
投票

我发现Groovy在JVM上作为一种类似C的脚本语言可以很好地工作。

不确定如何将它转换为C / C ++,但如果你有C / C ++代码需要调用,那么你可以轻松地使用JNI链接到它。

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


0
投票

JavaScript怎么样?我不满足你的所有要求(它只是弱类型),但它似乎遇到了很多:C类,通常的控制语句,运算符,数组,函数,你可以提供自己的API ......它甚至可以编译成Java,但是现代解释器非常善于及时编译它。如果您是用Java编写的,我强烈建议您查看Rhino


0
投票

这是完整的C,但可能对您有用。 Cibyl


0
投票

在阅读完所有答案之后,这就是我决定要做的事情。为了开始我的项目,我不会在开始时使用脚本。我要做的是使用Java编程,但不使用任何有问题的结构。

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

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.