32 位 handle 的一种生成方法

💡 原文中文,约1500字,阅读约需4分钟。
📝

内容提要

一个好的handle生成算法应满足需求,可以使用整数handle代替指针,使用自增id和hash表映射,避免碰撞,同时满足需求5,使用固定大小整数数组管理handle,回收时加一,避免重复分配,销毁handle时保证O(1)时间分配新handle,比自增方案更节省空间。若数组满可倍增size,但rehash过程复杂。使用该数据结构可将同类对象分配在一个大数组中,用handle索引,几乎没有额外开销。

🔎

延伸解读

为何要避免哈希表

文章指出,用自增id加哈希表映射handle到指针,虽然简单,但哈希表并非严格O(1),且冲突处理(如skynet中的线性探测)会破坏O(1)分配需求。新方案用固定大小数组和完美哈希,确保分配和回收都是常数时间,适合对实时性有要求的场景。

回收时高位加一的巧妙之处

每次回收handle时,将对应slot的高(32-n)位加一,生成的新id不会与已分配id重复。这避免了自增方案中因碰撞而跳过大量未使用数字的问题,更节省32位空间。但handle数值不再单调递增,可能回绕,需注意长期运行下的空间耗尽风险。

数组满时的扩展代价

文章建议预先规划上限,通常无需考虑数组满。若必须扩展,可倍增size,但rehash过程复杂:不仅要迁移有效handle,还需标记已用过的slot,防止新分配与历史id冲突。这增加了实现难度,可能影响性能。

适用场景与限制

该方案适合同时有效handle数量有限、且不频繁销毁创建少数handle的场景。若有效handle极多或频繁增删,32位空间可能不足。此外,它要求对象分配在大数组中,用handle索引,几乎无额外开销,但需注意数组大小与handle位数的权衡。

❓

Q&A

什么是handle生成算法的主要特点?

一个好的handle生成算法应使用整数handle而非指针,避免碰撞,并确保O(1)时间分配新handle。

为什么32位数字在长期运行的程序中可能不足?

在长期运行的程序中,有效handle的数量可能会超过32位数字的表示范围,导致空间耗尽。

如何避免handle的hash冲突?

通过使用固定大小的整数数组管理handle,并维护一个完美hash表,可以避免handle的hash冲突。

handle的回收是如何实现的?

在回收handle时,通过增加高位bits来确保新分配的handle不会与已分配的handle重复。

如果handle数组满了,应该如何处理?

可以考虑倍增数组的size,但rehash过程复杂,需要重新填充有效handle并标记可能重复的slots。

该算法相比自增方案有什么优势?

该算法更有效利用数字空间,避免了自增方案中可能的数字跳过,减少了空间耗尽的风险。

➡️

继续阅读