18

我目前正在开发一个用 Java 编写的项目。我们有一堆用 C/C++ 编写的算法(至少有几百个)需要合并到我们的项目中。我们的两个选择是使用 JNI 调用这段代码,或者用 Java 重写所有算法。

我知道使用 JNI 的后果,它会引发一系列全新的问题,这就是为什么甚至考虑用 Java 重写所有代码的原因。但是重写它的想法似乎......错误。据我所知,这些算法已经过测试并且可以正常工作,它们只是使用了错误的语言。

在这种情况下,JNI 会让这项任务变得简单吗?还是会比用 Java 重写代码更让人头疼?


编辑#1: 相关问题 - JNI 的有用性


编辑#2: 仅供参考 - 我们的 Java 项目并不意味着以任何方式可移植。这可能会消除 JNI 的缺点之一,因为它可能会降低可移植性。

4

11 回答 11

18

简单的答案是,如果代码将被大量调用并且性能很重要,那么将其转换为 Java。

更复杂的答案是:

  • 如果库很容易包装在 JNI 中,那么使用 JNI
  • 如果您对 C/C++ 代码的测试很容易转换为 Java,那么就去移植

我会做以下事情:

  • 采用其中一种算法并将其包装在 JNI 中
  • 采用相同的算法并将其转换为 Java
  • 看看哪个更痛苦
  • 如果速度很重要,那么分析两个版本并查看它们中的哪一个是可以接受的。
于 2009-03-04T18:51:15.400 回答
14

我认为答案在于调用 java 代码和调用的 C/C++ 代码之间的耦合量以及重写所需的工作量。如果您的 C 代码需要几个整数,进行一些繁琐的计算,然后返回另一个 int。使用 JNI。如果有很多复杂的来回,但算法相当简单,请重写它们。故障线是 JNI 连接。如果这会很复杂,那么您最终可能会编写更多的 JNI 接口代码而不是用于重写的算法代码。

于 2009-03-04T16:22:52.887 回答
13

如果“算法”封装得很好,也许可以通过 JNI 创建一些自动粘合代码?这将降低出错的风险(手动创建数百个单独的组件听起来很冒险),并使成本与要添加的算法数量或多或少无关。

重写数百个组件听起来很冒险,我绝对建议至少先更仔细地研究 JNI。

算法对项目的其余部分有什么 I/O 要求?如果它们是相当松散耦合的,也许可以将它们作为独立的独立程序运行,作为 Java 的子进程调用并使用例如 stdio 来共享数据?

于 2009-03-04T16:22:03.543 回答
9

我想您会发现使用 JNI 并不像在深入研究之前那样令人生畏。有一些重要的权衡和限制,但总的来说,JNI 运行良好,并且相当容易按照您的意图使用。

正如其他人所说,JNI 的最佳案例是:

  • 复杂的本机代码(如果该代码已被证明非常可靠,则加分)
  • Java 和本机代码之间的来回最小化(您希望最小化跨 JNI 层的行程)
  • 对本机代码的相当简单的调用接口(或者如果您是本机代码需要利用 Java 对象/方法,也可以返回 Java)

对于一组结构合理的本地组件,自动化或伪自动化创建 JNI 层绝对是可能的。如果要包装的组件数量很大,那将是非常值得的。

JNI 的好消息是您应该可以直接构建和测试此接口,例如,您应该能够从 Java 编写利用现有算法行为的测试用例,如果存在问题,它们很可能出现在 JNI层本身而不是算法(鉴于您对本机实现的信心)。

用 Java 重写大量 C/C++ 算法对我来说似乎比使用 JNI 风险更大。当然,如果不知道细节就很难衡量,但我可以想象影响算法实际实现的技术之间存在细微的差异,更不用说纯粹的工程工作和出错的风险了。

最后一个考虑因素是这些算法或相关组件的未来寿命。这组算法或多或少是完整的,还是您继续添加?无论哪种方式,是否有强有力的维护和/或未来发展的理由来支持一种技术而不是另一种?例如,如果其他一切都在 Java 中,所有新算法都将在 Java 中,并且团队中的几乎每个人几乎总是在 Java 中编码,那么从长远来看,用 Java 重新实现开始看起来更具吸引力。

然而,即使这样说,工作胜过一致。对于大量运行良好的原生组件,即使您要长期过渡到 Java,我仍然倾向于从 JNI 开始。

于 2009-03-04T22:36:25.033 回答
8

我仍然会认真考虑使用 JNI,特别是如果您能够最大限度地减少跨语言接口的数量。

例如,如果有一整套 C 函数都具有相同的输入和输出,但只是对输入执行不同的工作,则将其包装在单个 JNI 包装器中,并使用附加参数指定要使用的特定算法。

于 2009-03-04T16:23:29.640 回答
8

如果您打算用 Java 编写未来的项目,而不是 C/C++。我现在会咬紧牙关,将代码移植到java。你等待的时间越长,它就会变得越糟。在这种情况下,JNI 听起来很有吸引力,但是当其他人都在有趣的新 Java 项目上时,您将遇到必须有人维护 C++ 代码的问题。

如果这是一个一次性的 Java 项目,那么你为什么要用 Java 编写它?

于 2009-03-04T16:23:48.407 回答
3

C 和 Java 之间的桥梁很昂贵,因此如果您必须来回传递大量数据,那么调用 C 而不是 JNI 是没有好处的。

我已经看到 JNA 被列为 JNI 的合理替代方案,以类似反射的方式从 Java 调用 DLL/共享库中的方法要少得多。我还没有尝试过。

您可能需要考虑将 C 代码编译为可以用 Java 解释的 Linux MIPS 二进制文件。相当快,有点难以正确设置。见http://nestedvm.ibex.org/

您也可以使用 llvm 后端到 gcc 来编译为低级字节码,然后可以在 Java 中进行解释。这也是 Mac OS X 上的 iPhone SDK afaik 采用的方法。 http://llvm.org/

于 2009-03-04T19:09:20.053 回答
2

阅读 Joel 关于“偿还您的技术债务”的回执,然后立即转换它,而不是在未来几年支付利息然后还清。

也许重新打包的库也可以在更多地方使用。如果它们有用且复杂到足以让您考虑将它们保留在 C 中,那么一旦很好地封装在可重用、易于分发和易于理解的类中,它们一定会有更多用途。

于 2009-03-04T17:51:47.540 回答
2

我一直处于这种情况。我建议您习惯使用 Java 中可用的多线程数据结构。此时,您将不想再回到您的 C++ 数据结构。

您还将意识到,您可以使用更周到的技术、更好的多态性、适配器模式等更好地重写这些算法。

当您坐在 C/C++ 代码中时,您会想着要坚持下去。您正在考虑您投入的所有爱。几年后,您会意识到,坚持使用它并使用 JNI,您已经创建了一个怪物。每次您遇到进程崩溃时,您都会后悔使用 JNI。修复进程崩溃后,有人会想要修改 C++ 中的算法,而您会想要限制他们。他们会有一个方便的借口,比如“我还不习惯 Java”。

看看 Java 中的不可变数据结构。甚至不要考虑这些旧的 C/C++ 算法的执行速度。我发现我的新代码远远超过了我的任何旧 C/C++ 代码。

编写单元测试。使用 NetBeans。

亲切的问候,

-斯托什

于 2009-04-21T01:50:58.227 回答
1

试试 JNI。然后,如果遇到任何问题,您可以重写有问题的问题或使用 Web 服务包装 JNI 调用并将它们部署在不同的盒子或虚拟盒子上。

于 2009-03-04T16:23:33.727 回答
0

如果这仅适用于 Windows,并且它们已经在 DLL 中,那么可能会使用JNA 。(如果通过 JNA 调用函数的性能还不错)

于 2009-03-04T17:13:47.040 回答