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

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

在这种情况下,JNI 会让这个任务变得容易吗?或者说它会比用 Java 重写代码更让人头疼吗?


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


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

有帮助吗?

解决方案

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

更复杂的答案是:

  • 如果库很容易封装在 JNI 中,那么就使用 JNI
  • 如果您对 C/C++ 代码的测试可以轻松转换为 Java,那么请选择该端口

我会做以下事情:

  • 采用其中一种算法并将其包装在 JNI 中
  • 采用相同的算法并将其转换为 Java
  • 看看哪一个更痛苦
  • 如果速度很重要,那么分析两个版本并看看哪个版本是可以接受的。

其他提示

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

如果“算法”指的是包装精良,也许可以通过JNI创建一些自动胶水代码?这样可以减少出错的风险(手动创建数百个单独的组件听起来有风险),并使成本与要添加的算法数量无关,或多或少。

重写数百个组件听起来非常危险,我绝对建议至少首先更密切地调查JNI。

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

我想您会发现使用 JNI 并不像开始之前看起来那么令人畏惧。虽然存在一些重要的权衡和限制,但总的来说,JNI 运行良好,并且很容易按照您的意图进行利用。

正如其他人所说,JNI 的最佳情况是:

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

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

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

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

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

但即便如此,工作还是比坚持更重要。对于大量功能良好的本机组件,即使您打算长期过渡到 Java,我仍然倾向于从 JNI 开始。

我仍然认真考虑选择JNI,特别是如果你能够最大限度地减少跨语言界面的数量。

例如,如果有一堆C函数都具有相同的输入和输出,但只是对输入做了不同的工作,请将其包含在一个带有附加参数的JNI包装器中,以指定哪个特定的算法使用。

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

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

C和Java之间的桥梁很昂贵,所以如果你必须来回传递大量数据,那么通过JNI调用C就没有胜利。

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

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

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

阅读Joel关于“支付技术债务”的评论帖;然后继续转换它,而不是在接下来的几年里支付利息,然后付清。

也许重新打包的库也可以在更多地方使用。如果它们有用且复杂到足以让你考虑将它们保存在C中,那么它们必须在可重用,易于分发和可理解的类中很好地打包后才能使用它们。

我一直在这种情况下。我建议您习惯使用Java中提供的多线程数据结构。此时,您将不再需要返回到C ++数据结构。

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

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

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

编写单元测试。使用NetBeans。

亲切的问候,

-Stosh

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

如果这只是Windows,并且它们已经在DLL中,可能是 JNA 会有用。 (如果通过JNA调用函数的性能不是太差)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top