我正在进行一项规模庞大的数字计算项目。从一开始就一直在优化每个细节,因为我知道这很重要。通过性能分析,我的代码中有将近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 来替代空值检查,但是那一行的性能也受到了影响。
分支节点对象的代码如下:
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的低级工作原理不是很了解,但这可能是情况吗?
node.BranchData
存储在一个临时变量中,而不是在每次 while 循环迭代中加载该字段三次。 - Chris Sinclairnode = node.BranchData.Children[(1 + Math.Sign(inputs[node.BranchData.SplitInputIndex] - node.BranchData.SplitValue))/2];
替换掉if
语句。 - Sergey Kalinichenko