Lua 5.1 # 运算符和字符串比较(性能)

这是什么:

b = #{1,2,3}
c = 'deadbeef' == 'deadbabe'

在什么情况下b是O(n)或O(1)计算的?这种行为是一致的,还是像稀疏数组行为一样依赖上下文? 字符串比较是O(1)还是O(n)?我知道字符串是不可变的,Lua比较哈希值,但如果两个不同的字符串哈希到相同的值怎么办? 请不要回答“别担心低级行为,孩子”。我对低级行为很感兴趣。谢谢。

编辑

3)#运算符的结果是否储存在某处,还是每次我对相同的数组调用时都会计算?

点赞
用户2726734
用户2726734

Lua字符串存储在表中,以避免创建相同字符串的副本,因此每次创建字符串时,需要将其哈希并与具有相同哈希值的任何内容进行比较来创建它。

在创建后比较字符串对象的比较是 O(1),因为Lua已确保它们引用唯一的字符串,因此Lua只需比较基础指针。

由于所有字符串都是内部字符串,因此字符串相等性变成了 指针相等性

#define eqstr(a,b) ((a) == (b)) lstring.h

x = "deadbeef" -- 放入字符串表中
y = "deadbabe" -- 放入字符串表中
c = x == y    -- 比较指针

对于您提供的表格情况:

来自 ltabl.c:int luaH_getn (Table *t) 的实现:

t = {1, 2, 3} -- 需要创建一个表,哈希所有值等。
b = #t    -- 数组部分已满且没有哈希部分(因此#是数组大小),因此是常数时间。
t = [3] = nil
b = #t   -- 在数组部分内找到边界,二分搜索数组,b = 2。
b = #t   -- 另一次二分搜索
t = {1, 2, 3, [1000]=4}
b = #t   -- 数组已满,4不是哈希中的键,b = 3。
2014-11-12 13:33:52
用户3677376
用户3677376

表的长度在 O(log n) 中进行计算。算法大致如下:

  • 尝试通过取一步来找到一些整数索引映射到 nil。 步幅每次翻倍。 (如果在数组部分末尾找到了一个 nil 值,则可以跳过此部分。)
  • 当找到这样的索引时,在此索引和最后一个已知非 nil 索引之间使用分治算法来查找一个直接跟随 nil 值的非 nil 值交替的区间。

在此处查看详细信息 here。如果您具有连续的值序列,则此算法运作良好,但如果数组之间有空洞,则可能产生意外的结果。

编辑:内置的 # 运算符的结果不会被缓存,因此每次在表上使用 #(没有 __len 元方法)时,上述算法都会运行。

关于字符串比较(相等性): 较新的 Lua 版本在内部有两种字符串类型:短字符串(通常最多达到 40 字节)和长字符串。 如果长度匹配,长字符串使用 memcmp 进行比较,因此您可以得到 O(n)。 另一方面,短字符串则被“interned”,这意味着当你在 Lua 中创建某个短字符串时,会检查是否已存在具有相同内容的字符串。 如果有,则重用旧字符串对象,并且不会分配新字符串。 这意味着您可以比较内存地址以检查短字符串的相等性,这是 O(1)

2014-11-12 13:44:58