0

目前,我的代码如下所示:

    private List<Node> dirtyNodes = new List<Node> dirtyNodes();

    public void UpdateDirtyNodes()
    {
        while(dirtyNodes.Count > 0)
        {
            Node nodeToUpdate = dirtyNodes[0];
            nodeToUpdate.UpdateNode();
            dirtyNodes.Remove(nodeToUpdate);
        }
    }

    public void MarkNodeDirty(Node node)
    {
        if(!dirtyNodes.Contains(node))
        {
            dirtyNodes.Add(node);
        }
    }

    public void MarkNodeClean(Node node)
    {
        dirtyNodes.Remove(node);
    }

这是代码的性能关键部分,它比我想要的要慢,因为dirtyNodes.Contains在大多数情况下必须遍历整个数组。我想List用 a替换,HashSet因为它应该更快,但我不知道如何使它与UpdateDirtyNodes().

困难在于UpdateNode()可以随时添加或删除节点dirtyNodes,因此有点尴尬的while循环。有没有办法从 HashSet 中获取“第一个”值?顺序无关紧要,我只需要停留在 while 循环中,直到dirtyNodes为空,更新接下来出现的任何节点。

我宁愿避免使用 Linq,因为此代码将成为库的一部分,我不想强​​迫它们包含 Linq。

我怎样才能做到这一点?

4

2 回答 2

1

事实证明,直接使用枚举器非常容易:

    public void UpdateDirtyNodes()
    {
        while(dirtyNodes.Count > 0)
        {
            using(HashSet<Node>.Enumerator enumerator = dirtyNodes.GetEnumerator())
            {
                if(enumerator.MoveNext())
                {
                    Node nodeToUpdate = enumerator.Current;
                    nodeToUpdate.UpdateNode();
                    dirtyNodes.Remove(nodeToUpdate);
                }
            }
        }
    }

    public void MarkNodeDirty(Node node)
    {
        dirtyNodes.Add(node);
    }

我最初尝试了类似的方法,但不完全了解如何手动使用枚举器,但它不起作用。

它明显快于List(总帧时间快约 25-50%,具体取决于仅从那一次更改的情况),所以我很高兴。(不要担心下面屏幕截图中的 30MB 分配 - 我正在努力。)

在此处输入图像描述

于 2021-11-07T05:30:20.693 回答
0

在类中添加一个bool dirty字段Node。这是除了保留哈希集之外的。然后MarkNodeClean()不需要从 HashSet 中删除节点,从而减少了一些 CPU 周期。

如果您觉得在Node类中添加一个字段太“脏”(双关语),那么只需制作 aHashSet<(Node, bool)>而不是HashSet<Node>,但是您在分配和垃圾收集额外对象方面失去了性能,这并不理想,因为您的代码是性能-批判的。

UpdateDirtyNodes()将一次取一个节点,直到 HashSet 为空。取完每个节点后,它会查看布尔标志来判断该节点是否真的脏。

PS 你应该dirtyNodes.Clear();UpdateDirtyNodes(). 这是一个竞争条件。如果在while循环发现dirtyNodes.Count为0之后添加了一个节点,则dirtyNodes.Clear();清除该节点而不处理它。这是一个单独的错误,与您的问题无关。

于 2021-11-07T01:39:25.910 回答