优化SimpleTimingLib 当前实现的SimpleTimingLib并不是很好。OnUpdate处理程序执行任务的时间为O(n),如果你想在调度大量任务的插件中使用这个库,这是无法接受的。
使用以下代码:
schedule函数现在创建所有被调度(scheduled)任务的升序排序链表:
从代码中可以很容易地看到,这个操作现在是O(n),因为它可能会迭代整个链表,以找到插入它的正确位置。以下代码显示了新的OnUpdate处理程序:
它看起来像一个典型的O(n)操作,因为它似乎在整个数组上循环。当所有被调度任务都需要在当前帧(frame)中结束(due)时,它可能在O(n)模式下运行。但是,我们必须查看整体性能,即使在最坏的情况下,所有任务都需要在此帧中执行,在下一次调用OnUpdate时也会有一个空的链表。在几乎所有的调用中,函数将只查看第一个元素,因为一旦遇到尚未结束的任务,它就会取消循环。
SimpleTimeingLib现在看起来稍微复杂一些,但如果被许多插件使用,它会更快。库的用途是提供给多个插件使用,你必须假设一个库被另一个插件广泛使用。你不会想在这里浪费性能。 ●我们必须使用循环将所有参数插入到再利用表中。我们目前使用简单的{...},之前已经看到这比循环要快。 尽管有缺点,弱哈希表解决方案似乎仍然很有吸引力,因为你可能没想到next会成为这样一个性能大户(performance hog)。让我们看看下一步会有多糟糕。下面的代码创建了一个包含50000个条目的简单示例哈希表:
让我们测试一个从表中删除所有项的简单循环:
它的速度和预期的一样快,在我的笔记本电脑上大约0.03秒。现在让我们尝试使用以下代码。它也是删除表中的所有条目,但使用while循环和next(t, nil)检索表的第一个条目,直到表完全为空:
这个循环在我的笔记本电脑上需要5秒的CPU时间,因为下一步必须遍历整个表去找一个条目直到最后。这在一个只有几个条目的大表上需要花一些时间,如果一个短的周期内有很多计时器(哈希表增长),而之后的一个周期只有几个计时器(垃圾回收器删除了大部分计时器),那么存储再利用表的哈希表将会变成这样的表。 利用Userdata
你可能想知道使用普通表的优势在哪里。唯一的区别是内存的使用。如果表的唯一目的是通过元表转发访问,则表会分配32字节的内存,这是浪费的。userdata对象不分配任何不必要的内存,但其优势仍然非常小,通常不值得付出努力。 Userdata元方法(Userdata Metamethods)
我们可以通过添加以下代码来测试哈希表对象:
使用这样的表的一个缺点是,访问和设置其中的值会比常规表慢。这是因为每次访问或更改值时都有一个额外的函数调用。
不需要通过调用collectgarbage(“collect”) 来触发垃圾回收器,因为当脚本结束时,垃圾回收器还会使用终结器收集所有对象。这样做的原因是userdata对象可能已经打开系统资源(如文件),而终结器会关闭它们。因此,Lua必须确保调用所有终结器,即使脚本以错误终止。
它看起来很复杂,现在让我们看一个简单的例子。
总结 |