【问题标题】:Mixing OCaml and C: is it worth the pain?混合 OCaml 和 C:值得痛苦吗?
【发布时间】:2010-08-11 00:41:41
【问题描述】:

我面临的任务是构建一个新组件以集成到现有的大型 C 代码库中。该组件本质上是一种编译器,并且足够复杂,以至于我想用 OCaml 编写它(原因与here 相同)。我知道 OCaml-C 交互是可能的(根据manual 和这个tutorial),但它看起来有些痛苦。

我想知道的是这里的其他人是否尝试过大规模集成 OCaml 和 C 代码,他们发现了哪些意想不到的问题,以及他们最终是否得出结论认为他们会最好只用 C 编写新代码。

注意,我不是试图就函数式编程与命令式编程的优点展开辩论:假设我们假设 OCaml 恰好是我心目中工作的正确工具,而集成的潜在困难是唯一的问题。我也没有重写代码库其余部分的选项。

提供更多关于任务的细节:我需要实现的组件是某种查询优化器,它结合了我在加州大学戴维斯分校的小组正在研究的一些研究想法,并将集成到 PostgreSQL 中,以便我们可以运行实验。 (查询优化器本质上是一个编译器。)该组件将从 C 代码中调用,大部分独立运行,但会对其他 PostgreSQL 组件进行一定数量的调用以检索系统目录信息等内容,并构造一个复杂的C 数据结构(表示物理查询计划)作为输出。

对于这个有点开放式的问题,我深表歉意,但我希望社区可以为我省点麻烦 :)

谢谢,

TJ

【问题讨论】:

  • 出于好奇,您希望 C 必须解压缩什么 ocaml 类型?

标签: c postgresql ocaml


【解决方案1】:

很好的问题。您应该使用更好的工具来完成这项工作。

如果事实上您的意图是使用更好的工具来完成这项工作(并且您确定 lexx 和 yacc 会很痛苦),那么我有一些东西要与您分享;从 c 调用 ocaml 一点也不痛苦,反之亦然。大多数时候,我一直在编写调用 C 的 ocaml,但我也以其他方式编写了一些。它们大多是不返回结果的调试函数。虽然,后面和第四次的调用实际上是关于在 C 端打包和解包 ocaml value 类型。你提到的那个教程涵盖了所有这些,而且非常好。

我反对 Ron Savage 说你必须是语言专家。我记得从我工作的地方开始,在几个月内,不知道“函子”是什么,能够调用 C,并为数字配方和抽象数据类型编写了一千行 C,并且出现了一些问题(不是解包类型,而是抽象数据类型的垃圾收集),但它一点也不差。项目中的大部分内部循环都是用 C 编写的——利用了 SSE、外部库 (lapack)、更紧密的优化循环和一些内联的手动优化程序集。

我认为您可能需要具备设计大型项目以及划分功能部分和命令部分的经验。我真的会评估你要写多少 ocaml,以及你想传递给 C 什么样的值——我这么说是因为我害怕推荐给某人传递一个递归数据结构ocaml 到 C,实际上,这将是大量的解包元组及其内容,因此很可能会出现混乱和错误。

【讨论】:

  • 很好的答案。我应该强烈指出,这在很大程度上取决于与 C 集成痛苦(API 大小)相比应该完成的 OCaml 工作量。它适用于将任何新语言集成到任何现有项目中。
  • 这不仅是一个很好的答案,它甚至是我想听到的:)
【解决方案2】:

我写了一个相当复杂的 OCaml-C 混合程序。我对我发现的文档不足感到沮丧,最终我花了太多时间处理垃圾收集问题。但是,生成的程序可以运行并且速度很快。

我认为 OCaml-C 集成有一个地方,但要确保它是值得的。让程序通过套接字进行通信可能更简单(假设这样的 IO 操作不会消除您想要的性能)。用 C 语言编写整个内容可能更明智。

【讨论】:

    【解决方案3】:

    互操作性是静态类型语言独立实现的致命弱点,尤其是像 OCaml 这样没有 JIT 编译的语言。我自己使用 OCaml 超过 5 年的经验是,唯一可靠的绑定是跨简单 API 的,这些 API 只传递大型数组,例如拉帕克。即使是稍微复杂一点的绑定,比如与 FFTW 的绑定,也需要数年时间才能稳定下来,而其他的绑定,比如 OpenGL 和 GLU,仍然是一个未解决的问题。特别是,我发现了 OCaml 编译器的两位作者编写的绑定代码中的主要错误。如果他们不能把它做好,那么我们其他人就没有希望了......

    但是,一切都没有丢失。解决方案就是使用更松散的绑定。与其在 C 级别使用低级类型不安全接口处理互操作性,不如使用像 XML-RPC 这样的高级接口,通过字符串传递甚至通过套接字。这更容易做到正确,并且正如您所说,它将让您利用 OCaml 为该应用程序提供的巨大实际优势。

    【讨论】:

    • 使用 IPC 而不是编写绑定是一个经常被忽视的想法。为什么他们总是寻找复杂的解决方案?
    • 也经常被忽略:使用普通的命令行调用 + 管道而不是 IPC。如果调用 C 或 C++ 程序在任何实际意义上“没有”开销,则无需管理另一个长时间运行的进程。当然,这仅在您为粗粒度任务运行时才可行,即每秒或每分钟调用一次,而不是每毫秒调用一次的紧密循环。但是您也不应该在紧密循环中执行 FFI,因此这并不是真正的约束,而只是强制执行最佳实践:始终将紧密循环推到较低级别。
    【解决方案4】:

    我的经验法则是坚持现有代码库中使用的语言/模型/样式,以便未来的维护开发人员继承一组一致且易于理解的应用程序代码。

    我可以证明你所建议的唯一方法是:

    1. 您是 OCaml 的专家和 C 的新手(因此您的工作效率将提高 20 倍)
    2. 您之前已经成功地将它与 C 库集成(显然没有)

    如果您对 C 的了解比对 OCaml 更熟悉,那么您就失去了 OCaml 在编写编译器时更易于使用的任何“理论”收益——而且似乎您周围会有更多熟悉 C 的同行你比 OCaml。

    那是我的“脾气暴躁的老程序员”2 美分(过去只需要一分钱!)。

    【讨论】:

    • 我不明白你为什么要假设一个人必须是 OCaml 的专家和 C 的新手才能在 OCaml 的强项领域更高效地使用 OCaml。如果有一个 Web 组件,你会用 C 而不是 Python+HTML+Javascript 来编写所有这些吗?
    • 问题:Ron Savage 的回答实际上是否基于有关 OCaml 甚至 C 的任何知识/细节?
    • @Chuck - 因为在 1 个月而不是 5 个月内完成该功能可能值得为现有项目和 C 开发人员团队引入全新语言带来额外的复杂性、维护难题和风险。 1个月vs 2个月?不值得。
    • @Domingo - 不了解 OCaml,不了解 C 和许多其他语言,而且在没有架构或设计纪律的情况下继承摇摇欲坠的综合代码库的经验太多。
    • @Ron:我认为您所说的一般经验法则适用于生产代码,但我的项目与编写研究原型代码有关,其中维护和人员问题不太相关。 (我的问题对此不是很清楚。)在最好的情况下,原型非常好,有人必须用 C 重写代码才能将其放入真正的代码库中。但是,即使我们一开始就用 C 语言编写它,此时从头开始重写它可能会更好。 (“打算扔掉一个”...)
    猜你喜欢
    • 1970-01-01
    • 2014-08-18
    • 1970-01-01
    • 2010-12-24
    • 1970-01-01
    • 2014-02-21
    • 2016-01-25
    • 2012-02-21
    • 2016-02-02
    相关资源
    最近更新 更多