将 Haskell 用于大型实时系统:如何(如果?)?

Posted

技术标签:

【中文标题】将 Haskell 用于大型实时系统:如何(如果?)?【英文标题】:Using Haskell for sizable real-time systems: how (if?)? 【发布时间】:2010-11-18 19:42:14 【问题描述】:

我很想知道是否有可能将 Haskell 的强大功能应用于嵌入式实时世界,并在谷歌搜索中找到了 Atom 包。我假设在复杂的情况下,代码可能包含所有经典的 C 错误——崩溃、内存损坏等,然后需要追溯到原始的 Haskell 代码 造成了他们。所以,这是问题的第一部分:“如果你有使用 Atom 的经验,你是如何处理调试编译 C 代码中的低级错误并在 Haskell 原始代码中修复它们的任务的?”

我搜索了更多关于 Atom 的示例,this blog post 提到了生成的 C 代码 22KLOC(显然没有代码:),included example 是一个玩具。 This 和 this 引用有一些更实用的代码,但这就是结束的地方。我在主题中加上“相当大”的原因是,如果你能分享你在 300KLOC+ 范围内使用生成的 C 代码的经验,我最感兴趣。

由于我是 Haskell 新手,显然由于我未知的未知,我可能没有找到其他方法,因此将非常感谢该领域的任何其他自我教育指针 - 这是第二部分问题 - “在 Haskell 中进行实时开发的其他一些实用方法(如果)是什么?”。如果多核也在图片中,那是一个额外的加分:-)

(关于为此目的使用 Haskell 本身:根据我在 this blog post 中读到的内容,Haskell 中的垃圾收集和惰性使其在调度方面相当不确定,但可能在两年内发生了一些变化。Real world Haskell programming 问题关于 SO 是我能找到的最接近这个主题的)

注意: 上面的“实时”会更接近于“硬实时”——我很好奇是否可以确保主任务未执行时的暂停时间为不到 0.5 毫秒。

【问题讨论】:

【参考方案1】:

需要很长时间才能有一个适合小内存的 Haskell 系统,并且可以保证亚毫秒级的暂停时间。 Haskell 实现者社区似乎对这种目标不感兴趣。

人们对使用 Haskell 或类似 Haskell 的东西编译成非常高效的东西很感兴趣;例如,Bluespec 编译为硬件。

我认为它不能满足您的需求,但如果您对函数式编程和嵌入式系统感兴趣,您应该了解Erlang。

【讨论】:

+1 用于 bluespec 链接。将它与 Lava 进行比较会很有趣 - 从我读过的内容来看,它具有相似的功能。 Erlang:是的,那是我开始窥探的另一件事,但它似乎有点“更柔和”的实时性。 你也可以看看 Ocaml。如果您希望将其用于算法,它是功能性的,甚至支持惰性求值,但不会强制对所有内容进行惰性求值。我似乎记得听说过它被用于实时系统,尽管我自己没有这样做。无论如何,它应该比 Erlang 更快更小。 OCaml、Erlang 和 Haskell 都用于软实时。例如,没有人保证响应时间。在我们在工作中使用的一个系统中,我们很高兴在 Haskell 中的网络设备上有大约 1 毫秒的响应时间,所以这是一种球场。调整 GC 设置以最小化噪音也很有用。 感谢您提及 OCaml。我玩 Ocaml 代码的主观感觉有点复杂。添加两个数字的简单示例需要我记住额外的语言特征,我觉得这是一种开销,而使用 Haskell 的实验要求我记住可以以更简洁的方式表达事物的语言特征(例如列表推导) - 或者完全是非常特殊的野兽(单子)。这是主观的,我需要获得更多的经验才能获得更多的理解——但第一印象很难抗争。 :)【参考方案2】:

我不认为 Haskell 或其他垃圾收集语言非常适合硬实时系统,因为 GC 倾向于将其运行时摊销为短暂的暂停。

用 Atom 编写并不完全是用 Haskell 编程,因为 Haskell 在这里可以被视为纯粹是您正在编写的实际程序的预处理器。

我认为 Haskell 是一个很棒的预处理器,使用像 Atom 这样的 DSEL 可能是创建大型硬实时系统的好方法,但我不知道 Atom 是否符合要求。如果没有,我很确定有可能(并且我鼓励任何这样做的人!)实现一个 DSEL。

拥有像 Haskell 这样非常强大的低级语言预处理器为通过代码生成实现抽象打开了一个巨大的机会之窗,当实现为 C 代码文本生成器时,这些抽象要笨拙得多。

【讨论】:

我很好奇您是否有使用这种组合的工作经验。就像我说的 - 元编程方面确实看起来很诱人,但我担心元错误及其与较低级别的“普通”错误的关系。由于在现场你只会有 C 代码的符号,乍一看,将它们展开回更高级别的代码似乎很棘手。 > Atom 可能是创建大型硬实时系统的好方法,如果它提供语法、变量、函数和类型系统,它就不是真正的“预处理器”。到那时,它就真正成为一种具有自定义代码生成器的语言,就像 Atom 一样。至于创建大型系统:Atom 用于在美国城市街道上行驶的公共汽车和垃圾车。请参阅伊顿在 CUFP 的演讲。如果将 Haskell EDSL 投放街头还没有准备好投入生产,我不知道是什么。 @dons:好的,由于您的评论,这至少在我的书中也得到了 +1。 Muchas gracias 分享实践经验。是时候让我休息一下,做更多的练习了。【参考方案3】:

在 Galois,我们使用 Haskell 做两件事:

软实时(操作系统设备层、网络),其中 1-5 毫秒的响应时间是合理的。 GHC 生成快速代码,并为调整垃圾收集器和调度程序以获得正确的时间提供大量支持。 对于真正的实时系统,EDSL 用于为提供更强时序保证的其他语言生成代码。例如。 Cryptol、Atom 和 Copilot。

所以要小心区分 EDSL(Copilot 或 Atom)与宿主语言(Haskell)。


一些关键系统示例,在某些情况下还包括实时系统,由 Galois 生成,由 Haskell 编写或生成。

EDSL

Copilot: A Hard Real-Time Runtime Monitor -- 用于实时航空电子设备监控的 DSL Equivalence and Safety Checking in Cryptol -- 用于关键系统加密组件的 DSL

系统

HaLVM -- 用于嵌入式和移动应用程序的轻量级微内核 TSE -- 跨域(安全级别)网络设备

【讨论】:

太棒了,非常感谢!是的,我了解用作代码生成器的 EDSL 和宿主语言本身之间的区别——如果我在问题的文本中不清楚,请原谅。我将其归入“haskell”类别的原因是,使用像 Haskell 这样更严格的语言可能(?)避免更多“机械”错误,如边界检查、双重释放等,假设足够的语义在EDSL - 这是我试图了解它是真是假的猜测之一。【参考方案4】:

安德鲁,

是的,通过将生成的代码返回到原始源来调试问题可能会很棘手。 Atom 提供了一种探测内部表达式的方法,然后由用户决定如何处理这些探测。对于车辆测试,我们构建了一个发射器(在 Atom 中)并通过 CAN 总线将探头输出。然后,我们可以捕获这些数据,对其进行格式化,然后使用 GTKWave 等工具在后处理或实时中查看它。对于软件仿真,探针的处理方式不同。不是从 CAN 协议获取探测数据,而是对 C 代码进行挂钩以直接提升探测值。然后在单元测试框架(与 Atom 一起分发)中使用探测值来确定测试是通过还是失败并计算模拟覆盖率。

【讨论】:

【参考方案5】:

我一直在玩弄 Atom。这很酷,但我认为它最适合小型系统。是的,它在卡车和公共汽车上运行并实现现实世界的关键应用程序,但这并不意味着这些应用程序一定很大或很复杂。它确实适用于硬实时应用程序,并且竭尽全力使每次操作都花费完全相同的时间。例如,不是 if/else 语句有条件地执行可能在运行时间上不同的两个代码分支之一,它有一个“mux”语句,它总是在有条件地选择两个计算值之一之前执行两个分支(所以总无论选择哪个值,执行时间都相同)。除了通过 Atom monad 传递的 GADT 值强制执行的内置类型(与 C 相比)之外,它没有任何重要的类型系统。作者正在开发一个分析输出 C 代码的静态验证工具,这很酷(它使用 SMT 求解器),但我认为 Atom 将受益于更多的源代码级功能和检查。即使在我的玩具大小的应用程序(LED 手电筒控制器)中,我也犯了一些新手错误,对包更有经验的人可能会避免这些错误,但这导致了错误的输出代码,我宁愿被编译器捕获而不是通过测试。另一方面,它仍然是 0.1 版。所以毫无疑问,改进即将到来。

【讨论】:

+1,非常感谢分享。让我们期待下一个版本! :-)

以上是关于将 Haskell 用于大型实时系统:如何(如果?)?的主要内容,如果未能解决你的问题,请参考以下文章

如何在纯 Haskell 中编写简单的实时游戏循环?

如何在资源有限的 Haskell 中解析大型 XML 文件?

如何在 PHP 博客上突出显示 Haskell 语法

hystrix 概述

如何构建一个稳定可靠安全的大型工业机械远程监控系统?

Haskell中的模块化算法