Saya memiliki pohon keputusan biner yang sangat penting untuk kinerja, dan saya ingin memfokuskan pertanyaan ini pada satu baris kode. Kode untuk iterator pohon biner ada di bawah ini dengan hasil dari menjalankan analisis kinerja terhadapnya.
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 adalah bidang, bukan properti. Saya melakukan ini untuk mencegah risiko tidak sebaris.
Kelas BranchNodeData adalah sebagai berikut:
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;
}
Seperti yang Anda lihat, while loop / null check sangat berpengaruh pada kinerja. Pohonnya besar, jadi saya berharap mencari daun akan memakan waktu cukup lama, tetapi saya ingin memahami jumlah waktu yang tidak proporsional yang dihabiskan untuk satu baris itu.
Saya sudah mencoba:
- Memisahkan cek Null dari sementara - cek Null itulah yang menjadi hit.
- Menambahkan bidang boolean ke objek dan memeriksanya, tidak ada bedanya. Tidak peduli apa yang dibandingkan, masalahnya adalah perbandingan.
Apakah ini masalah prediksi cabang? Jika ya, apa yang dapat saya lakukan? Jika ada?
Saya tidak akan berpura-pura memahami CIL , tetapi saya akan mempostingnya untuk semua orang sehingga mereka dapat mencoba mengambil beberapa informasi darinya.
.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
Sunting: Saya memutuskan untuk melakukan tes prediksi cabang, saya menambahkan identik jika dalam beberapa saat, jadi kita punya
while (node.BranchData != null)
dan
if (node.BranchData != null)
di dalam itu. Saya kemudian menjalankan analisis kinerja terhadapnya, dan butuh enam kali lebih lama untuk menjalankan perbandingan pertama seperti yang dilakukan untuk mengeksekusi perbandingan kedua yang selalu mengembalikan true. Jadi sepertinya ini memang masalah prediksi cabang - dan saya rasa tidak ada yang bisa saya lakukan ?!
Edit lainnya
Hasil di atas juga akan terjadi jika node.BranchData harus dimuat dari RAM untuk pemeriksaan while - kemudian akan di-cache untuk pernyataan if.
Ini adalah pertanyaan ketiga saya tentang topik serupa. Kali ini saya fokus pada satu baris kode. Pertanyaan saya yang lain tentang hal ini adalah:
while(true) { /* current body */ if(node.BranchData == null) return node; }
. Apakah itu mengubah sesuatu?
while(true) { BranchNodeData b = node.BranchData; if(ReferenceEquals(b, null)) return node; node = b.Child2; if (inputs[b.SplitInputIndex] <= b.SplitValue) node = b.Child1; }
Ini node. BranchData
hanya akan diambil sekali.
BranchNode
properti. Silakan coba gantinode.BranchData != null
ReferenceEquals(node.BranchData, null)
. Apakah ada bedanya?