Performans açısından kritik bir ikili karar ağacım var ve bu soruyu tek bir kod satırına odaklamak istiyorum. İkili ağaç yineleyicinin kodu, ona karşı performans analizi çalıştırmanın sonuçlarıyla birlikte aşağıdadır.
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;
}
BranchData bir özellik değil, bir alandır. Bunu satır içi olmama riskini önlemek için yaptım.
BranchNodeData sınıfı aşağıdaki gibidir:
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;
}
Gördüğünüz gibi while döngüsü / null kontrolü, performansa büyük bir darbe vuruyor. Ağaç çok büyük, bu yüzden bir yaprak aramanın biraz zaman almasını beklerdim, ancak o satırda harcanan orantısız zamanı anlamak istiyorum.
Denedim:
- Boş çekini süre ile ayırmak - isabet olan Boşluk çekidir.
- Nesneye bir boole alanı eklemek ve buna karşı kontrol etmek, hiçbir fark yaratmadı. Neyin karşılaştırıldığı önemli değil, sorun olan karşılaştırma.
Bu bir şube tahmin sorunu mu? Öyleyse bu konuda ne yapabilirim? Eğer birşey?
CIL'i anlıyormuş gibi yapmayacağım , ancak bunu herkesin anlaması için göndereceğim , böylece ondan bazı bilgiler almaya çalışabilirler.
.method public hidebysig
instance class OptimalTreeSearch.ScTreeNode GetNodeForState (
int32 rootIndex,
float32[] inputs
) cil managed
{
// Method begins at RVA 0x2dc8
// Code size 67 (0x43)
.maxstack 2
.locals init (
[0] class OptimalTreeSearch.ScTreeNode node,
[1] class OptimalTreeSearch.BranchNodeData b
)
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Collections.Generic.List`1<class OptimalTreeSearch.ScRootNode> OptimalTreeSearch.ScSearchTree::RootNodes
IL_0006: ldarg.1
IL_0007: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<class OptimalTreeSearch.ScRootNode>::get_Item(int32)
IL_000c: ldfld class OptimalTreeSearch.ScTreeNode OptimalTreeSearch.ScRootNode::TreeNode
IL_0011: stloc.0
IL_0012: br.s IL_0039
// loop start (head: IL_0039)
IL_0014: ldloc.0
IL_0015: ldfld class OptimalTreeSearch.BranchNodeData OptimalTreeSearch.ScTreeNode::BranchData
IL_001a: stloc.1
IL_001b: ldloc.1
IL_001c: ldfld class OptimalTreeSearch.ScTreeNode OptimalTreeSearch.BranchNodeData::Child2
IL_0021: stloc.0
IL_0022: ldarg.2
IL_0023: ldloc.1
IL_0024: ldfld int32 OptimalTreeSearch.BranchNodeData::SplitInputIndex
IL_0029: ldelem.r4
IL_002a: ldloc.1
IL_002b: ldfld float32 OptimalTreeSearch.BranchNodeData::SplitValue
IL_0030: bgt.un.s IL_0039
IL_0032: ldloc.1
IL_0033: ldfld class OptimalTreeSearch.ScTreeNode OptimalTreeSearch.BranchNodeData::Child1
IL_0038: stloc.0
IL_0039: ldloc.0
IL_003a: ldfld class OptimalTreeSearch.BranchNodeData OptimalTreeSearch.ScTreeNode::BranchData
IL_003f: brtrue.s IL_0014
// end loop
IL_0041: ldloc.0
IL_0042: ret
} // end of method ScSearchTree::GetNodeForState
Düzenleme: Dal tahmin testi yapmaya karar verdim, süre içinde ise bir özdeş ekledim, yani
while (node.BranchData != null)
ve
if (node.BranchData != null)
onun içinde. Daha sonra buna karşı performans analizi çalıştırdım ve her zaman doğru olan ikinci karşılaştırmayı yürütmek için olduğu gibi ilk karşılaştırmayı yürütmek altı kat daha uzun sürdü. Yani bu gerçekten bir dal tahmini sorunu gibi görünüyor - ve bu konuda yapabileceğim hiçbir şey olmadığını tahmin ediyorum ?!
Başka Bir Düzenleme
Yukarıdaki sonuç, node.BranchData'nın while denetimi için RAM'den yüklenmesi gerekmesi durumunda da ortaya çıkar - daha sonra if ifadesi için önbelleğe alınır.
Bu, benzer bir konudaki üçüncü sorum. Bu sefer tek bir kod satırına odaklanıyorum. Bu konudaki diğer sorularım:
while(true) { /* current body */ if(node.BranchData == null) return node; }
. Herhangi bir şeyi değiştirir mi?
while(true) { BranchNodeData b = node.BranchData; if(ReferenceEquals(b, null)) return node; node = b.Child2; if (inputs[b.SplitInputIndex] <= b.SplitValue) node = b.Child1; }
Bu node. BranchData
yalnızca bir kez alınır .
BranchNode
mülkün uygulamasını gösterin . Lütfen değiştirmeyi deneyinnode.BranchData != null
ReferenceEquals(node.BranchData, null)
. hiç fark yapar mı?