在C#中迭代树的微优化

时间:2013-05-07 09:51:18

标签: c# optimization binary-tree

我正在开展大量数字运算项目。我从一开始就一直在优化所有内容,因为我知道这很重要。进行性能分析时,我的代码在一个函数中花费了近40%的生命 - 二叉树迭代器。

        public ScTreeNode GetNodeForState(int rootIndex, float[] inputs)
        {
0.2%        ScTreeNode node = RootNodes[rootIndex].TreeNode;

24.6%       while (node.BranchData != null)
            {
0.2%            BranchNodeData b = node.BranchData;
0.5%            node = b.Child2;
12.8%           if (inputs[b.SplitInputIndex] <= b.SplitValue)
0.8%                node = b.Child1;
            }

0.4%        return node;
        }

任何C#优化专家都有进一步优化的提示吗?所有的比较都是花车。我知道理论上它应该没关系,但我使用的是字段而不是属性,所以要确保优化。在这里节省一点钱可以减少这个过程。

请不要回复说“这些优化在现实世界中并不重要” - 因为在这种情况下他们会这样做。 : - )

编辑:我已经将代码更新为我现在遵循以下注释的内容,并在每行代码的性能分析输出中添加。如你所见,主要杀手是空检查 - 为什么?我尝试在节点上使用布尔标志IsLeaf而不是null检查,但在该行上它的性能相同。

分支节点对象的代码如下:

public sealed class BranchNodeData
{
    /// <summary>
    /// The index of the data item in the input array on which we need to split
    /// </summary>
    internal int SplitInputIndex = 0;

    /// <summary>
    /// The value that we should split on
    /// </summary>
    internal float SplitValue = 0;

    /// <summary>
    /// The nodes children
    /// </summary>
    internal ScTreeNode Child1;
    internal ScTreeNode Child2;
}

另一个编辑:还有更多思考......我想知道为什么这条线

BranchNodeData b = node.BranchData;

注册执行率为0.2%,空对比线注册17.7%。我猜这是一个分支预测失败?虽然这种比较被多次击中,并且几乎总是返回true,但是CPU很难预测它什么时候会返回false。我对CPU的低级工作情况不是很了解,但情况可能就是这样吗?

3 个答案:

答案 0 :(得分:3)

只需重写一些代码。它可能会有所帮助,因为它可以避免至少两次跳跃。

public ScTreeNode GetNodeForState(int rootIndex, float[] inputs)
{

    ScTreeNode node = RootNodes[rootIndex].TreeNode;

    while (node.BranchData != null)
    {
        BranchNodeData b = node.BranchData;
        node = b.Child2;
        if (inputs[b.SplitInputIndex] <= b.SplitValue))
            node = b.Child1;
    }

    return node;

}

答案 1 :(得分:0)

BranchNodeData看起来像一个引用类型。它仅占运行时的0.2%,因为它只是指向已存在的数据,而不是实际复制或分配任何内容。

你可能会在空检查上受到这样的打击,因为CLR必须进行强制转换才能检查你粘贴的密封类。检查无效你不一定要追求什么。有很多方法可以修改该类,为您提供一个布尔值来检查,这不需要那么多的计算能力。老老实实地说,这是你的ScTreeNode类可以提供的东西。

答案 2 :(得分:0)

考虑到关于缓存的其他答案中提出的观点,但与空检查无关,请尝试对BranchNodeData字段的引用进行排序,以便第一个引用允许将所有以下字段加载到缓存。

也就是说,我认为当SplitInputIndexSplitValue时,抖动或CPU不够智能,无法向后缓存Child1Child2BranchNodeData在当前代码中首先引用。

因此,要么更改set; if ... overwrite;课程中字段的顺序,要么将if ... else更改为{{1}}。