我正在开展大量数字运算项目。我从一开始就一直在优化所有内容,因为我知道这很重要。进行性能分析时,我的代码在一个函数中花费了近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的低级工作情况不是很了解,但情况可能就是这样吗?
答案 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
字段的引用进行排序,以便第一个引用允许将所有以下字段加载到缓存。
也就是说,我认为当SplitInputIndex
为SplitValue
时,抖动或CPU不够智能,无法向后缓存Child1
,Child2
和BranchNodeData
在当前代码中首先引用。
因此,要么更改set; if ... overwrite;
课程中字段的顺序,要么将if ... else
更改为{{1}}。