【问题标题】:Rewrite C code in Java or use JNI?用Java重写C代码还是使用JNI?
【发布时间】:2009-04-21 09:50:58
【问题描述】:

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

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

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


编辑 #1: 相关问题 - Usefulness of JNI


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

【问题讨论】:

  • 这些算法是否足够通用以至于其他人可能已经编写了它们?
  • 不,不幸的是,它们根本不是很笼统。

标签: java java-native-interface


【解决方案1】:

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

更复杂的答案是:

  • 如果库很容易被 JNI 封装,那么就使用 JNI
  • 如果您对 C/C++ 代码的测试很容易转换为 Java,那么就选择移植版

我会做以下事情:

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 相关思想:当我考虑用 Java 重写 C 代码时,我通过查看原始 C 代码并查看使用了多少指针魔法来评估需要多少努力。在某些情况下,过度使用指针“快捷方式”会使代码变得很糟糕(并非不可能!)变成整洁的 OO 代码。
    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

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

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

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

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

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

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

      不过,即便如此,工作还是始终如一。对于大量运行良好的原生组件,即使您要长期过渡到 Java,我仍然倾向于从 JNI 开始。

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

          • 这不是一次性的 Java 项目,C/C++ 代码是旧的。我从来没有从这个角度考虑过。好建议。
          【解决方案7】:

          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/

          【讨论】:

          • LLVM 生成位码,它与 JVM 字节码无关。 LLVM 的级别要低得多,要在 JVM 或 CLR 中运行,您需要使用仍处于起步阶段的 VMKit 项目。
          • ørn:我认为,大量数据可以通过引用传递。您的意思是说传递数据是瓶颈吗?您的意思是频繁调用 JNI 函数可能是瓶颈?
          • 除非您非常仔细地设计数据结构(这需要动手经验),否则您将需要在 C 数据结构和 Java 对象之间传输数据。这个过程是“昂贵的”。
          【解决方案8】:

          阅读 Joel 在“Paying down your technical debt”上的收据,然后立即转换,而不是在未来几年支付利息然后还清。

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

          【讨论】:

            【解决方案9】:

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

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

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

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

            编写单元测试。使用 NetBeans。

            亲切的问候,

            -斯托什

            【讨论】:

              【解决方案10】:

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

              【讨论】:

                【解决方案11】:

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

                【讨论】:

                  猜你喜欢
                  • 2011-09-07
                  • 2011-02-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-06-26
                  • 1970-01-01
                  • 2010-11-23
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多