【问题标题】:How do you pre-size an array in Lua?如何在 Lua 中预先设置数组的大小?
【发布时间】:2010-09-12 13:45:28
【问题描述】:

我有一个 Lua 程序似乎比它应该的要慢。我怀疑问题是我一次向关联数组添加一个值,并且表每次都必须分配新内存。

似乎确实有一个 table.setn 函数,但在 Lua 5.1.3 下它失败了:

stdin:1: 'setn' is obsolete
stack traceback:
        [C]: in function 'setn'
        stdin:1: in main chunk
        [C]: ?

我从 Google 搜索中得知,该功能在 Lua 5.1 中已被弃用,但我找不到什么(如果有的话)取代了该功能。

你知道如何在 Lua 中预先设置表格的大小吗?

或者,当您将对象添加到表时,是否有其他方法可以避免内存分配?

【问题讨论】:

  • 前几天我读这篇文章是为了好玩,但如果你对 lua 性能感兴趣,你应该看看这篇关于 lua 实现的论文,它介绍了相当多的内部数据结构. lua.org/doc/jucs05.pdf

标签: optimization lua


【解决方案1】:

让我更专注于你的问题:

将值添加到关联数组 一次一个

Lua 中的表是关联的,但以数组形式 (1..N) 使用它们是优化的。他们在内部有双面。

所以.. 如果您确实是关联地添加值,请遵循上述规则。

如果您使用索引 1..N,您可以通过设置 t[100000]= 来强制重新调整一次大小。这应该一直有效,直到 Lua 源中指定的优化数组大小限制 (2^26 = 67108864)。之后,一切都是关联的。

附言旧的 'setn' 方法只处理数组部分,所以它对关联用法没有用(忽略那些答案)。

p.p.s.您是否研究过保持 Lua 高性能的一般技巧?即知道表的创建并重用表而不是创建新表,使用“本地打印=打印”等以避免全局访问。

【讨论】:

  • 我一般没有研究过 Lua 的性能,但我绝对感兴趣。其实我只是问了一个问题:stackoverflow.com/questions/154672/…
  • 这个答案不正确(不再是?),根据“Lua 性能提示”(在这里获取lua.org/gems),Lua 分析表并将值放入哈希部分,如果数组部分有小于 array_N/2,因此构造 t = {}; t[100000] = true 将产生一个空数组部分和一个包含一个元素的哈希部分。
【解决方案2】:
static int new_sized_table( lua_State *L )
{
    int asize = lua_tointeger( L, 1 );
    int hsize = lua_tointeger( L, 2 );
    lua_createtable( L, asize, hsize );
    return( 1 );
}

...

lua_pushcfunction( L, new_sized_table );
lua_setglobal( L, "sized_table" );

然后,在 Lua 中,

array = function(size) return sized_table(size,0) end

a = array(10)

作为快速运行此程序的技巧,您可以将 C 添加到 lua.c

【讨论】:

  • 默认不提供这个有什么原因吗?比如说,在表库中。
【解决方案3】:

我不认为你可以 - 它不是一个数组,它是一个关联数组,就像 perl 哈希或 awk 数组。

http://www.lua.org/manual/5.1/manual.html#2.5.5

我认为你不能从 Lua 方面有意义地预设它的大小。

如果你在 C 端分配数组,那么

void lua_createtable (lua_State *L, int narr, int nrec);

可能是你需要的。

创建一个新的空表并推送 它放到堆栈上。新表有 为 narr 数组预分配的空间 元素和 nrec 非数组元素。 当您 确切地知道有多少元素 表会有。否则你可以使用 lua_newtable 函数。

【讨论】:

  • 另一方面,.NET 的 System.Collection.Hashtable 确实有一个带有容量参数的构造函数。
  • 在内部,Lua 表的整数键实际上存储在一个数组中。如您所说,其余部分存储在关联数组中。
【解决方案4】:

还有一个内部的luaL_setn,你可以编译Lua,这样 它公开为 table.setn。但是好像没什么用 因为代码似乎没有做任何预扩展。

(另外setn上面注释的setn和数组部分有关 一个 Lua 表,你说你正在使用该表作为关联 数组)

好的部分是即使你一个一个地添加元素,Lua 也不会 以这种方式增加数组。相反,它使用了更合理的策略。你还在 为更大的数组获得多个分配,但性能优于 每次都获得新的分配。

【讨论】:

  • 对于一般情况,这是一个合理的策略,但对于这个特定的程序,我确切地知道表需要多大。
【解决方案5】:

虽然这没有回答您的主要问题,但它回答了您的第二个问题:

或者,当您将对象添加到表时,是否有其他方法可以避免内存分配?

如果您在自定义应用程序中运行 Lua,正如我猜测的那样,因为您进行 C 编码,我建议您用 Loki 的小值分配器替换分配器,它减少了我的内存分配 100 倍以上。这通过避免往返内核来提高性能,并使我成为一个更快乐的程序员:)

无论如何我尝试了其他分配器,但它们更通用,并提供不利于 Lua 应用程序的保证(例如线程安全和大对象分配等),还编写自己的小对象分配器可能需要一周的编程和调试才能做到恰到好处,在寻找可用的解决方案后,Loki 的分配器是我为这个问题找到的最简单和最快的方法。

【讨论】:

    【解决方案6】:

    如果您在代码中使用特定数量的项目声明表,如下所示:

    local tab = { 0, 1, 2, 3, 4, 5, ... , n }
    

    然后 Lua 将创建表,其内存已分配给至少 n 个项目。

    但是,Lua 使用 2x 增量内存分配技术,因此向表中添加项目很少会强制重新分配。

    【讨论】:

      猜你喜欢
      • 2016-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-26
      • 2021-06-28
      • 2014-03-22
      相关资源
      最近更新 更多