确定性句柄分配算法

时间:2009-09-11 20:47:25

标签: c++ algorithm data-structures tree

我正在尝试找到一种有效的确定性方法,以一种无限期运行的方式分配32位句柄。一个简单的递增计数器将无法工作,因为它最终会循环。不可能扩展到64位,因为这些值将用于期望32位值的网络协议中。

它适用于实时系统,因此必须具有确定性和快速性。虽然它最终会出现在SQLite数据库中,但我不能在循环后对每个键进行强力测试,例如...

我认为我需要的是某种范围树,它知道所有先前分配的句柄(在启动时填充这个很好)。这似乎是一种常见的(ish)问题,但它不是通过boost或STL解决的问题。

任何指针?

编辑:一些额外的澄清。我希望在任何时候都能在系统中拥有200k活动句柄的顺序。句柄随机删除。

3 个答案:

答案 0 :(得分:3)

您不能分配超过2 ^ 32。但是如果它们被释放,您可以重新分配使用过的句柄,问题是跟踪空闲句柄。

树是存储空闲句柄的好方法。每个节点都有一个最低和最高句柄,左子树包含小于最低句柄的句柄,右子树包含大于最高句柄的句柄。

一个例子是:

            6-9
            / \
           2   15
          / \
         0   4

如果释放句柄,它将存储在树中。例如,如果释放10,则树看起来像:

            6-10
            / \
           2   15
          / \
         0   4

如果释放了句柄5,您可以选择优化树,因为4可以添加到5-10节点中:

            5-10
            / \
           2   15
          / \
         0   4

要:

            4-10
            / \
           2   15
          / 
         0  

分配句柄,搜索带有1个句柄的叶节点并将其从树中删除。如果没有带有1个句柄的叶子,只需使用叶子并减少未连接到父对象的一侧:

         4-10
        /
      1-2

在上面的示例中,我们分配1而不是2,因为如果释放3,则可以将其与4组合,并且希望保持节点数尽可能低。

下面是伪代码算法。有些部分留给读者:

Node = ( int lowest, highest; [Node] left, right)


Node.Allocate() 
  if TryFindLonelyLeaf(handle)    return handle;
  else
    return FindLeafHandle(NULL);

Node.TryFindLonelyLeaf(handle)
  if (left == NULL & right == NULL) 
    if (lowest == highest)
      handle == lowest;
      return TRUE;
    else
      return FALSE;
  if (left != NULL & left.TryFindLonelyLeaf(handle))
    if (left.lowest == handle) 
      left == NULL; // Free node
    return TRUE;
  elsif (right != NULL & right.TryFindLonelyLeaf(handle))
    if (right.lowest == handle)
       right = NULL; // Free node
    return TRUE;
  else
    return FALSE;

Node.FindLeafHhandle(parent)
  if (left == NULL & right == NULL) 
    if (parent == NULL | parent.right == this) 
      handle = lowest;
      lowest++;
    else
      handle = highest;
      highest--;
    return handle;
  else if (left != NULL) 
    return left.FindLeafHandle();
  else // right != NULL
    return right.FindLeafHandle();

Node.DeAllocate(handle) 
  Assert(handle<lowest or handle>highest);
  if (handle == lowest-1)
    lowest = CompactLeft(handle);
  elsif (handle == lowest+1)
    highest = CompactRight(handle); 
  elsif (handle<lowest)          
    if (left == NULL)
      left = (handle, handle, NULL, NULL);
    else
      left.DeAllocate(handle);
  elsif (handle>highest)
    if (right == NULL)
      right = (handle, handle, NULL, NULL);
    else
      right.DeAllocate(handle);
  else ERROR           

Node.CompactLeft(handle)
  if (highest == handle-1) 
    handle = lowest;
    // deallocate node and replace with left subtree
    return handle;
  elsif (right != NULL) 
    return right.CompactLeft(handle)
  else
    return handle;

Node.CompactRight(handle)
  if (lowest == handle+1) 
    handle = highest;
    // deallocate node and replace with right subtree
    return handle;
  elsif (left != NULL) 
    return left.CompactRight(handle)
  else
    return handle;

答案 1 :(得分:3)

如果内存不是问题,那么您可以维护一个免费句柄列表。释放一个时,将其添加回空闲列表的末尾。

一开始,您可以将所有ID添加到空闲列表中,但效率很低。

您可以做的优化是保持一个值,即可用的最小ID,以及空闲列表。因此,当列表为空时,您将添加一些id(从您维护的最小id值开始)到空闲列表并更新最小id值。

答案 2 :(得分:0)

如果这个问题只是“我怎样才能快速,安全地计算一个独特的,当前未使用过的数字”,那么一个位表会给你这个。

对于200K唯一编号的顺序,200.000 / 8 =所需的字节数= 25000,这只是为了跟踪的25KB内存。

当然,如果您需要在内存中跟踪与使用中句柄相关的数据,那么您还需要其他内容。

另一种解决方案,可能是更快地获得新句柄,将保留一堆未使用的句柄。每次需要一个手柄时,从堆栈中弹出一个。

您可以使用设定的数字为堆栈设定种子,但算法也是如此,如果您尝试从空堆栈中弹出一个值,您只需通过递增一个不断增加的值来生成一个新值。

因为你说你在任何给定的时间都会有大约200K的句柄,所以这个堆栈永远不会比握住那么多的句柄大,所以你可以用数组轻松处理它。 200K 32位堆栈将消耗800.000字节,大约781KB的内存。