actboy168 / YDWE

A Tool to Help the Creation of Warcraft III Map
GNU General Public License v3.0
463 stars 139 forks source link

忽然想到一个效率低下 但是能解决 key=table pairs异步的解决方案 #214

Open w4454962 opened 1 year ago

w4454962 commented 1 year ago

pairs之所以异步 是因为 没办法给table分配一个 所有玩家都一致的 index,

1.32 在每个table创建时 自动分配一个独立的index ,但是因为本地玩家/异步事件 会在异步时机创建table,进而导致之后分配的索引异步,从而失败,

所以问题的本质 就是 每个table 需要一个不受其他位置创建table的影响, 而在自己的位置有自己的计数的一个index

所以我想到的解决方案就是 在创建table时, 根据当前lua堆栈 获取一个索引分配器, 进行递增分配索引

所以 最终结果是 index的类型 由 uint64_t 来完成

uint64_t 高位 4字节 由lua调用堆栈 进行哈希算法 由4字节哈希值完成 uint64_t 低位 4字节 由lua调用堆栈 映射的分配器 进行递增 来完成

这样异步环境下 调用 某个函数 生成的table 跟同步环境下 调用 同一个函数 生成的table 他们之间的 index 都是分别独立 不会互相干扰

这样同步产生的table 作为key 进行遍历 就不会异步了

这个方案唯一的问题就是 获取lua调用堆栈的效率过低, 尤其是 lua5.3之后 debug.traceback的效率 相比5.1下降了10倍 所以差最后这个步骤进行闭环。

actboy168 commented 1 year ago

最简单的办法不是避免这种用法吗

w4454962 commented 1 year ago

你脱离作图1线 异步创建table 是很难避免的, 除非将所有异步的接口分离,单独一个虚拟机, 同步接口单独一个虚拟机, 否则一定存在异步创建table的情况的, 比如 message.hook 你自己就异步创建了table push给lua了。。。

actboy168 commented 1 year ago

我是说避免遍历key是gc对象的table

w4454962 commented 1 year ago

总是会有这种需求的。 再者遍历 key=string是硬需求, 但是在编写过程中很容易疏忽 导致一个表里 不止是string 有可能存在其他的obj 这时候就异步了。 lua产生异步的概率还是非常高的

actboy168 commented 1 year ago

key是string,只需要统一string的hash值就可以解决

w4454962 commented 1 year ago

我知道, 但是在编写的过程中,很容易疏忽, 让一个表的key不完全是string ,而且一些新人入坑的 完全不知道这个问题,在编写完几万行代码后发现地图异步,几乎如同海底捞针。

actboy168 commented 1 year ago

检查pairs和next,不符合要求就抛错

w4454962 commented 1 year ago

这是不得以而为之的办法。 dream big

w4454962 commented 1 year ago

就1楼的方案 专门看了一下 luaL_traceback 的源码 发现效率低的原因有2个

1 字符串格式化 字符串连接 这些操作 2 5.3之后 为了读取 文件返回表时 函数字段名称 递归了package.loaded里所有的表 去匹配相同函数值的字段名 导致效率低下

所以针对这2个问题 去掉一些无用信息 就遍历调用栈 取关键信息进行哈希 节省字符串格式化/连接 这些操作 就可以极大的提高 获取调用栈哈希值 信息的效率。

应该还是可行性的 等后面有空的时候我再实际操作一个版本试试。

sumneko commented 1 year ago

同位置也会有异步的table鸭

local res
for i = 1, jass.GetPlayerID(jass.GetLocalPlayer()) do
    res = {}
end

实际业务中,可能是一个异步的事件执行同一段代码,然后将创建的表保存在全局里以后用。

w4454962 commented 1 year ago

异步创建的表 混合 同步创建的表 一起作为key 进行遍历 产生的异步 确实可能存在,但是情况还是会比较少的,

大部分情况下 是在同步环境下 创建的表对象 需要在同步的情况下进行pairs 仅仅为满足这个需求。

当然最安全的情况 就是分2个虚拟机 用中间代码进行交互。 但是会带来使用上的负担 影响便捷性

sumneko commented 1 year ago

我觉得,与其做一个99%安全的功能,不如完全禁止这个功能。英萌当年就做了 safePairsunsafePairs 的区分,业务层使用默认的 pairs 就是会检查是否安全的,如果一定想要遍历需要使用另一个API,让用户知道这么做有风险,这样用户才会自己仔细检查。