【问题标题】:How can I execute Lua functions from C++ without having to find them each time?如何从 C++ 执行 Lua 函数而不必每次都找到它们?
【发布时间】:2018-11-11 08:31:54
【问题描述】:

我是 Lua 的新手,正在研究如何在 C++ 程序中嵌入/使用它。我有基本的机制工作,但对尽可能快地执行代码感兴趣。所以我正在加载/编译稍后可以使用的 lua 代码(包含多个函数)。

现在假设我想从 C++ 调用一个名为“add”的 lua 函数。据我所知,我必须使用 lua_getglobal() 函数,该函数似乎可以找到并将“添加”的字节码推送到 Lua 堆栈。我可以消除查找部分,即我可以保留对函数“add”的字节码的引用,以便在需要使用它时可以简单地将其推送到堆栈中吗?我不知道当有数千个 lua 函数时 lua_getglobal() 的效率如何,我也不喜欢过早优化,但我正在尝试构建一个硬实时系统(是的,实际的物理截止日期,不仅仅是快)如果我不得不匆忙调用数十万次'add',每次都必须寻找函数似乎真的很浪费。

另外,为了好奇,实际的字节码是被推送(即复制)到堆栈上还是只是对其的引用?我希望是后者。

提前致谢

【问题讨论】:

  • "我不知道 lua_getglobal() 在有数千个 lua 函数的情况下有多高效,我也不喜欢过早优化" 过早优化是任何优化在知道要优化的原始操作或整个程序的性能的情况下。所以你似乎在自相矛盾。
  • 另外,如果您正在构建“硬实时系统”,那么您应该远离任何具有垃圾收集功能的语言。就像,你知道的,Lua。
  • 感谢您的回复。回复:过早优化。是的,我知道它是什么。但同样情况下,任何有经验的人都知道合法的阻塞点,例如可以执行数百万次的嵌套“for”循环的内部。不打算立即摆脱“查找”部分,但知道是否可以消除它很有用。
  • 回复:GC,是的,我知道 GC 问题。那里有一些确定性的 GC,但我对 Lua 的了解还不够,无法知道它是否足够好。几天前我才开始研究嵌入式脚本语言,还有其他几种语言要检查,比如我还没有研究过的 ChaiScript。
  • "不打算立即摆脱“查找”部分,但知道是否可以消除它很有用。" 你不明白的是每次您在 Lua 脚本 中执行 add(...) 时,Lua 解释器都会执行与您的 C 代码完全相同的工作(除非 add 是局部变量)。因此,除非您调用的唯一 Lua 函数是不执行任何操作的函数,否则在 C 中获取函数的性能是无关紧要的。这是过早优化错误的另一个原因:您找错地方了。所以无论你使用什么lua_ref 噱头都无济于事。

标签: lua evaluation


【解决方案1】:

我能否消除查找部分,即我能否保留对函数“add”的字节码的引用,以便在需要使用时将其简单地推入堆栈?

您可以尝试的一件事是使用 luaL_Ref 创建对该函数的引用。 该引用应该可以更快地访问 - 似乎它使用数字索引表,因此它可能比带有字符串键的哈希表更快。

在 C 语言中,尽可能使用 lua_ref()。 lua_ref() 在速度方面的行为类似于局部变量。 - Lua,pre v4.0

http://lua-users.org/wiki/OptimisationCodingTips

我尝试做一些测试,将 1000000000 次迭代的 tonumber 推送到堆栈,结果是 lua_getglobal 需要 24 秒,带有 ref 的 rawgeti 需要 12 秒,2 个堆栈和 lua_pushvalue + lua_xmove 需要 11 秒,并且使用 lua_pushvalue 和相同的状态和位于堆栈顶部的实际功能需要 6 秒。 在此处查看代码:http://pastebin.com/n8Q0uVx1

http://www.lua.org/manual/5.3/manual.html#luaL_ref http://www.lua.org/manual/5.3/manual.html#luaL_unref

【讨论】:

  • 非常有趣。除非我遗漏了什么,否则我对 Lua 实现的担忧是编译的函数(字节块,大概不会改变?)每次都必须复制到堆栈中,而不是仅仅推送地址(比如说)代码。如果你的函数很大,那么多次复制似乎效率很低。
  • 啊,请阅读 Nicol Bolas 的第二条评论,他在底部指出(如果我理解正确的话)该字节没有被复制。
【解决方案2】:

lua_getglobal 的核心只是进行表查找,在功能上与lua_getfield 没有区别。 Lua 表是哈希表,因此无论条目数如何,查找都是按常数时间摊销的。

这并不意味着这样的提取速度非常快。但是获取或多或少的性能并不取决于表中有多少不同的项目。

至于能够将函数存储在某处以便更快地访问......不。嗯,不合理。

Lua 值不能存储在 C 代码中。虽然您可以从 Lua 值中获取数字或字符串,但您无法以任何有意义的方式获取 Lua 函数。您可以致电lua_topointer,但无法撤消该转换。

但是,您可以做的是从主要的lua_State 创建一个附属lua_State。然后,您将使用函数加载其 stack。使用堆栈索引比进行哈希表查找要快。当需要调用此函数时,您可以使用lua_xmove 将函数从附属lua_State 移动到要执行的主状态。

当然,这样做会使您的代码更难阅读。一个简单的lua_getglobal(L, "add")lua_pushvalue(Funcs, some_int)/lua_xmove(L, Funcs) 调用更有意义。使用全局表还允许您更改该值,从而使对其的所有访问都产生一个新函数。

此外,还不清楚lua_State 的堆栈允许有多大。所以你可能没有足够的空间来完成这项工作。

最后,访问函数不会复制实际内存。但是将垃圾回收对象放入堆栈意味着必须更新其 GC 数据以注意堆栈正在引用它。所以它不仅仅是一个指针副本。

【讨论】:

  • Nicol,非常感谢您提供非常有用的反馈。可读性问题可以理解,但是可以仔细注释这些内容,并在一些 C++ 内联方法中隔离这些内容来管理它们。然而,阅读 lua_xmove,我不清楚这有什么帮助。文档表明这实际上是从一个 lua_State 弹出到另一个。那不会从辅助堆栈中删除该函数吗?
  • @David 关键点可能是在另一个堆栈中有值,然后用 pushvalue 在那里复制它,然后移动那个副本。这样你就不会从第二个堆栈中完全删除它。然而,我的小测试代码表明,使用 2 个堆栈和 xmove VS luaL_ref 的差异只会提高大约 1.2 倍。更新了我的测试代码以显示这一点。
  • 知道了。请理解,由于我才刚刚开始研究 Lua(除其他外),因此我对适当的习语并不十分熟悉。当项目不在顶部时可以访问/删除的堆栈的概念是我不知道的(就此而言,我也不认为这样的结构称为堆栈)
  • @David:“我也不认为这样的结构被称为堆栈” 这是 Lua 的一个有趣元素。 Lua 堆栈兼作 Lua 的 注册文件。因此,如果 Lua 脚本需要访问之前创建的某个局部变量,那么它可以向下进入“堆栈”并检索它,这与它在顶部时没有什么不同。它只是使用不同的索引。它比您想象的要方便得多。
  • 是的,我毫不怀疑它非常方便 ---- 但它不应该被称为堆栈,因为从技术上讲,它不是,这就是你使用 lua_xmove 的建议令人困惑的原因大部头书。根据定义,堆栈有 push、pop、isempty 操作,也许 top 用于窥视。如果还有其他东西,它就不是堆栈,即使它包含堆栈功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多