32 位 handle 的一种生成方法
内容提要
一个好的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。
该算法相比自增方案有什么优势?
该算法更有效利用数字空间,避免了自增方案中可能的数字跳过,减少了空间耗尽的风险。