C# 如何有效地池化节点树的对象?

C# How to pool the objects of a node tree efficiently?

我有一个节点 class,它只包含值类型属性和一个引用类型:它是父节点。在执行树搜索时,这些节点会在很短的时间内创建和销毁数十万次。

public class Node
{
   public Node Parent { get; set; }
   public int A { get; set; }
   public int B { get; set; }
   public int C { get; set; }
   public int D { get; set; }
}

树搜索看起来像这样:

public static Node GetDepthFirstBest(this ITree tree, Node root)
{
   Node bestNode = root;
   float bestScore = tree.Evaluate(root);

   var stack = new Stack<Node>();
   stack.Push(root);

   while(stack.Count > 0)
   {
      var current = stack.Pop();

      float score = tree.Evaluate(current);
      if (score > bestScore)
      {
         bestNode = current;
         bestScore = score;
      }

      var children = tree.GetChildren(current);
      foreach(var c in children) { stack.Push(c); } 
   }

   return bestNode;
}

因为这是在具有非常旧的 GC 的 Mono 运行时中完成的,所以我想尝试合并节点对象。但是,我不知道如何知道节点对象何时可以安全地 return 到池中,因为仍在使用的其他节点可能会将其作为父节点引用。在搜索结束时,最好的节点被 returned 并通过回溯其祖先形成一个节点列表。如果有用的话,我可以完全控制节点在树中的创建方式。

我可以尝试和实施哪些选项?

如果您的节点是结构体,请尝试使用 ref 关键字,这样可以避免每次将节点传递给函数时都复制该节点。

因此:

struct Node
{
      object obj;
      Node children;
}

public void DoStuffWithNode(ref Node pNode){...Logic...}

所以,幸运的是,如果您正在做一个 Depth-First-Search,这看起来会更容易一些。任何时候到达叶节点,都有两种可能性:该叶节点是当前最深树的一部分,或者不是。

如果不是,则意味着 return 将此节点加入池是安全的。如果是,那意味着我们可以 return 旧树中不在我们自己的祖先链中的任何节点返回到我们的池中。

现在,如果我们不是叶节点,在我们检查完 children 之前,我们不知道是否可以释放。然后,一旦检查了我们所有的 children,我们就会发现我们的 children 中是否有任何人说他们是当前最好的。如果是这样,我们保持自我

这确实意味着我们正在做更多的检查。

这是一些 sudo 代码:

List bestNodes;

bool evalNode(node, score)
{
    if (childCount == 0)
    {
        if (score > bestScore)
        {
            bestScore = score;
            bestNode = node;
            bestNodes.Add(node);

            return true;
        }
        else
        {
            freeNode(this);

            return false;
        }
    }
    else
    {
        bool inLongest = false;
        foreach (child in children)
        {
            inLongest = evalNode(child, score + 1) || inLongest;
        }

        if (!inLongest)
        {
            freeNode(node);
        }
        else
        {
            free(bestNodes[score]);
            bestNodes[score] = node;
        }             

        return inLongest;
    }
}