我正在一个海量号码处理项目。我从一开始就一直在优化一切,因为我知道这很重要。做性能分析我的代码将其生命中几乎40%的时间花在一个函数中 - 二叉树迭代器。微型优化迭代通过一棵树在C#
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%。我猜这是分支预测失败?虽然这种比较被多次击中,并且几乎总是返回正确的,但它使CPU很难预测何时会返回错误。我并不是很了解CPU的低级工作,但是这可能是这种情况吗?
其中一个想法是预取状态(字典)并在数据更改时更新它们。这样,当需要获取节点的状态时,操作将在O(1) – 2013-05-07 09:57:48
中完成。从C++的角度来看......我不知道在C#中有多少可能,但是当你走过一棵树时,你已经失去了缓存局部性。如果你的所有物体都是通过记忆分散的,那么局部性就消失了。如果你的树很密集,可以考虑将它存储为一个数组或者使用一个内存池(如果你可以用C#来做)。你也会在这里成为分支预测的受害者。不知道你能否做得更多。 – paddy 2013-05-07 10:00:12
一个_minor_事情可能是将'node.BranchData'存储在一个临时变量中,而不是每次循环迭代加载该字段三次。 – 2013-05-07 10:01:55