35

当前选择: lua-jit。令人印象深刻的基准测试,我已经习惯了这种语法。编写高性能 ABI 需要仔细考虑我将如何构建我的 C++。

其他感兴趣的问题

  1. Gambit-C 和 Guile 作为可嵌入语言
  2. Lua 性能提示(可以选择使用禁用的收集器运行,并在处理运行结束时调用收集器始终是一个选项)。

背景

我正在开发一个实时大容量(复杂)事件处理系统。我有一个 DSL,它在源头表示事件结构的模式、存储格式、某些特定于域的构造、触发内部事件(以构建和驱动通用处理),以及对总是发生的某些处理步骤进行编码。

DSL 看起来与 SQL 非常相似,事实上我正在使用 berkeley db(通过 sqlite3 接口)来长期存储事件。这里重要的部分是事件的处理是基于集合完成的,比如 SQL。然而,我得出的结论是,我不应该向 DSL 添加通用处理逻辑,而是嵌入 lua 或 lisp 来处理这个问题。

处理核心是围绕 boost::asio 构建的,它是多线程的,rpc 是通过协议缓冲区完成的,事件使用协议缓冲区 IO 库进行编码——即,事件不是使用协议缓冲区对象构造的,它们只是使用相同的编码/解码库。我将创建一个包含行的数据集对象,这与数据库引擎在内存集中的存储方式非常相似。DSL 中的处理步骤将首先得到处理,然后提交给通用处理逻辑。

无论我使用什么可嵌入脚本环境,我的处理核心中的每个线程都可能需要它自己的嵌入式语言环境(如果你正在做多线程工作,lua 至少需要它)。

问题

目前的选择是在 lisp ECL 和 lua 之间。请记住,性能和吞吐量是一项强大的要求,这意味着非常需要最小化内存分配:

  1. 如果你处于我的位置,你会选择哪种语言?

  2. 有没有我应该考虑的替代方案(不要建议没有可嵌入实现的语言)。也许是 Javascript v8?

  3. lisp 是否更适合域?我不认为 lua 和 lisp 在它们提供的方面有什么不同。叫我出来 :D

  4. 我应该考虑其他任何属性(如下面的属性)吗?

  5. 我断言任何形式的嵌入式数据库 IO(请参阅下面的示例 DSL 了解上下文)都会使脚本语言调用相形见绌,而且选择其中任何一种都不会给整体吞吐量增加太多开销。我在正确的轨道上吗?:D

所需属性

  1. 我想将我的数据集映射到一个 lisp 列表或 lua 表上,并且我想最小化冗余数据副本。例如,如果两个表具有相同的形状,则从一个数据集中将一行添加到另一个应尝试使用引用语义。

  2. 我可以保证在进行 lua/lisp 调用时,作为输入传递的数据集不会改变。如果可能,我希望 lua 和 lisp 强制不更改数据集。

  3. 在嵌入式调用结束后,数据集应该被销毁,创建的任何引用都需要替换为副本(我猜)。

DSL 示例

为了您的观看乐趣,我附上了一个 DSL,这样您就可以了解我想要实现的目标。注意:DSL 不显示通用处理。

// Derived Events : NewSession EndSession
NAMESPACE WebEvents
{
  SYMBOLTABLE DomainName(TEXT) AS INT4;
  SYMBOLTABLE STPageHitId(GUID) AS INT8;
  SYMBOLTABLE UrlPair(TEXT hostname ,TEXT scriptname) AS INT4;
  SYMBOLTABLE UserAgent(TEXT UserAgent) AS INT4;  

  EVENT 3:PageInput
  {
    //------------------------------------------------------------//
    REQUIRED 1:PagehitId              GUID
    REQUIRED 2:Attribute              TEXT;
    REQUIRED 3:Value                  TEXT; 

    FABRRICATED 4:PagehitIdSymbol     INT8;
    //------------------------------------------------------------//

    PagehitIdSymbol AS PROVIDED(INT8 ph_symbol)
                    OR Symbolise(PagehitId) USING STPagehitId;
  }

  // Derived Event : Pagehit
  EVENT 2:PageHit
  {
    //------------------------------------------------------------//
    REQUIRED 1:PageHitId              GUID;
    REQUIRED 2:SessionId              GUID;
    REQUIRED 3:DateHit                DATETIME;
    REQUIRED 4:Hostname               TEXT;
    REQUIRED 5:ScriptName             TEXT;
    REQUIRED 6:HttpRefererDomain      TEXT;
    REQUIRED 7:HttpRefererPath        TEXT;
    REQUIRED 8:HttpRefererQuery       TEXT;
    REQUIRED 9:RequestMethod          TEXT; // or int4
    REQUIRED 10:Https                 BOOL;
    REQUIRED 11:Ipv4Client            IPV4;
    OPTIONAL 12:PageInput             EVENT(PageInput)[];

    FABRRICATED 13:PagehitIdSymbol    INT8;
    //------------------------------------------------------------//
    PagehitIdSymbol AS  PROVIDED(INT8 ph_symbol) 
                    OR  Symbolise(PagehitId) USING STPagehitId;

    FIRE INTERNAL EVENT PageInput PROVIDE(PageHitIdSymbol);
  }

  EVENT 1:SessionGeneration
  {
    //------------------------------------------------------------//
        REQUIRED    1:BinarySessionId   GUID;
    REQUIRED    2:Domain            STRING;
    REQUIRED    3:MachineId         GUID;
    REQUIRED    4:DateCreated       DATETIME;
    REQUIRED    5:Ipv4Client        IPV4;
    REQUIRED    6:UserAgent         STRING;
    REQUIRED    7:Pagehit           EVENT(pagehit);

    FABRICATED  8:DomainId          INT4;
    FABRICATED  9:PagehitId         INT8;
    //-------------------------------------------------------------//

    DomainId  AS SYMBOLISE(domain)            USING DomainName;
    PagehitId AS SYMBOLISE(pagehit:PagehitId) USING STPagehitId;

    FIRE INTERNAL EVENT pagehit PROVIDE (PagehitId);
  } 
}

该项目是博士研究项目的一个组成部分,并且是/将是免费软件。如果您有兴趣与我合作(或贡献)这个项目,请发表评论:D

4

4 回答 4

36

我非常同意@jpjacobs 的观点。 Lua是嵌入的绝佳选择,除非您需要一些关于 lisp 的非常具体的内容(例如,如果您的数据特别好地映射到 cons-cells)。

顺便说一句,我使用 lisp 已经很多年了,而且我非常喜欢 lisp 语法,但是这些天我通常会选择 Lua。虽然我喜欢 lisp语言,但我还没有找到一个 lisp实现,它可以像 Lua 那样在嵌入式使用中捕捉到特性/小巧/可用性的完美平衡。

卢阿

  1. 非常小,无论是源代码还是二进制文件,比许多更流行的语言(Python 等)小一个数量级或更小。由于 Lua 源代码非常小而且简单,如果您想避免添加外部依赖项,将整个 Lua 实现包含在源代码树中是完全合理的。

  2. 非常快。Lua 解释器比大多数脚本语言快得多(同样,一个数量级并不少见),LuaJIT2是一些流行的 CPU 架构(x86、arm、mips、ppc)的非常好的 JIT 编译器。使用 LuaJIT 通常可以将速度提高另一个数量级,并且在许多情况下,结果接近 C 的速度。LuaJIT 也是标准 Lua 5.1 的“直接”替代品:无需更改应用程序或用户代码即可用它。

  3. LPEG。LPEG 是 Lua 的“解析表达式语法”库,它允许非常简单、强大、快速的解析,适用于大型和小型任务;它是 yacc/lex/hairy-regexps 的绝佳替代品。[我使用 LPEG 和 LuaJIT 编写了一个解析器,它比我尝试模拟的 yacc/lex 解析器快得多,而且创建起来非常简单直接。] LPEG 是 Lua 的一个附加包,但是很好-值得得到(这是一个源文件)。

  4. 有一个很棒的C 接口,这使得从 C 调用 Lua 或从 Lua 调用 C 变得很愉快。对于大型/复杂 C++ 库的接口,可以使用 SWIG 或多种接口生成器中的任何一种(当然也可以使用 Lua 的简单 C 接口与 C++)。

  5. 具有自由许可(“类 BSD”),这意味着 Lua 可以嵌入到专有项目中,如果您愿意,并且与 FOSS 项目的 GPL 兼容。

  6. 非常非常优雅。它不是 lisp,因为它不是基于 cons-cells,但它显示出来自像 scheme 之类的语言的明显影响,具有直截了当且有吸引力的语法。与 scheme 一样(至少在它的早期版本中),它倾向于“最小化”,但在平衡可用性和可用性方面做得很好。对于有 lisp 背景的人(比如我!),很多关于 Lua 的内容看起来都很熟悉,而且“有意义”,尽管存在差异。

  7. 非常灵活,元表等特性允许轻松集成特定领域的类型和操作。

  8. 具有简单、吸引人且平易近人的语法。对于现有的 lisp 用户来说,这可能不是 lisp 的优势,但如果您打算让最终用户编写脚本,这可能是相关的。

  9. 专为嵌入而设计,除了体积小、速度快之外,还具有增量 GC 等各种功能,这使得在这种情况下使用脚本语言更加可行。

  10. 拥有悠久的历史,以及负责任和专业的开发人员,他们在过去 2 年中对语言的发展表现出良好的判断力。

  11. 拥有一个充满活力和友好的用户社区。

于 2011-10-28T09:54:48.700 回答
6

您没有说明您使用的是什么平台,但如果它能够使用LuaJIT 2,我肯定会这样做,因为执行速度接近已编译代码的速度,并且与 C 代码的接口变得更加容易FFI 库。

我不知道其他可嵌入的脚本语言,所以我无法真正比​​较它们能做什么,以及它们如何处理表格。

Lua 主要与引用一起工作:所有函数、用户数据、表都通过引用使用,并在下一次 gc 运行时在没有对数据的引用时收集。字符串是内部化的,因此某个字符串只在内存中存在一次。要考虑的事情是您应该避免创建并随后丢弃大量表,因为这会减慢 GC 周期(如您引用的 Lua gem 中所述)

为了解析您的代码示例,我会看一下LPEG 库

于 2011-10-28T08:05:21.727 回答
2

有许多选项可用于实现高性能嵌入式编译器。一个是 Mono VM,它自然而然地在其上实现了数十种已经制作好的高质量语言,而且它非常可嵌入(看看 Second Life 是如何使用它的)。也可以使用 LLVM - 看起来你的 DSL 并不复杂,所以实现一个 ad hoc 编译器并不是什么大问题。

于 2011-10-28T09:17:35.267 回答
2

我碰巧在一个项目上工作,它的某些部分与您的项目相似,它是一个在 Win-CE、Android、iOS 上运行的跨平台系统,我需要最大化跨平台代码,C/C++ 与嵌入式语言是一个不错的选择。这是我与您的问题相关的解决方案。

  1. 如果你处于我的位置,你会选择哪种语言?

我项目中的DSL和你的类似。为了性能,我用 Yacc/Lex 编写了一个编译器来将 DSL 编译为二进制文件以供运行时使用,并编写了一堆 API 来从二进制文件中获取信息,但是当 DSL 语法中有一些修改时很烦人,我需要同时修改编译器和 API ,所以我放弃了DSL,转成XML(不要直接写XML,一个定义好的schema值得),我写了一个通用编译器将XML转换为lua表,用lua重新实现API。通过这样做,我得到了两个好处:可读性和灵活性,而没有明显的性能下降。

  1. 有没有我应该考虑的替代方案(不要建议没有可嵌入实现的语言)。也许是 Javascript v8?

在选择lua之前,我考虑过Embedded Ch(主要用于工控系统),嵌入式lisp和lua,最后lua脱颖而出,因为lua与C很好的结合,lua社区繁荣,lua易于学习队员。关于 Javascript v8,如果在嵌入式实时系统中使用,它就像使用蒸汽锤来破解坚果。

  1. lisp 是否更适合域?我不认为 lua 和 lisp 在它们提供的方面有什么不同。叫我出来 :D

对于我的领域,lisp 和 lua 在语义方面具有相同的能力,它们都可以轻松处理基于 XML 的 DSL,或者您甚至可以编写一个简单的编译器将 XML 转换为 lisp 列表或 lua 表。他们都可以轻松处理域逻辑。但是 lua 更好地与 C/C++ 集成,这就是 lua 的目标。

  1. 我应该考虑其他任何属性(如下面的属性)吗?

单独或与团队成员一起工作也是解决方案选择的一个权重因素。如今,没有多少程序员熟悉类似 lisp 的语言。

  1. 我断言任何形式的嵌入式数据库 IO(请参阅下面的示例 DSL 了解上下文)都会使脚本语言调用相形见绌,而且选择其中任何一种都不会给整体吞吐量增加太多开销。我在正确的轨道上吗?:D

这里是编程语言性能列表,这里是计算机组件访问时间列表。如果您的系统受 IO 限制,则脚本的开销不是关键点。我的系统是 O&M(Operation & Maintenance)系统,脚本性能微不足道。

于 2013-11-18T15:34:01.947 回答