ฉันมีโครงสร้างการตัดสินใจไบนารีที่สำคัญด้านประสิทธิภาพและฉันต้องการเน้นคำถามนี้ไปที่โค้ดบรรทัดเดียว โค้ดสำหรับตัววนซ้ำต้นไม้ไบนารีอยู่ด้านล่างพร้อมผลลัพธ์จากการรันการวิเคราะห์ประสิทธิภาพเทียบกับมัน
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 เป็นฟิลด์ไม่ใช่คุณสมบัติ ฉันทำสิ่งนี้เพื่อป้องกันความเสี่ยงที่มันจะไม่อินไลน์
คลาส BranchNodeData เป็นดังนี้:
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;
}
อย่างที่คุณเห็นการตรวจสอบ while loop / null เป็นผลดีอย่างมากต่อประสิทธิภาพ ต้นไม้มีขนาดใหญ่ดังนั้นฉันคาดว่าการค้นหาใบไม้จะต้องใช้เวลาสักพัก แต่ฉันต้องการเข้าใจระยะเวลาที่ไม่สมส่วนที่ใช้ไปกับบรรทัดนั้น
ฉันพยายามแล้ว:
- การแยกการตรวจสอบ Null ออกจากขณะ - เป็นการตรวจสอบ Null ที่เป็น Hit
- การเพิ่มฟิลด์บูลีนให้กับออบเจ็กต์และตรวจสอบกับสิ่งนั้นมันไม่ได้สร้างความแตกต่าง ไม่สำคัญว่าจะมีการเปรียบเทียบอะไร แต่การเปรียบเทียบที่เป็นปัญหา
นี่เป็นปัญหาการทำนายสาขาหรือไม่? ถ้าเป็นเช่นนั้นฉันจะทำอย่างไร ถ้าทุกอย่าง?
ฉันจะไม่แสร้งทำเป็นเข้าใจCILแต่ฉันจะโพสต์ให้ใครก็ตามที่ทำเพื่อให้พวกเขาพยายามดึงข้อมูลบางส่วนออกมา
.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
แก้ไข:ฉันตัดสินใจที่จะทำการทดสอบการทำนายสาขาฉันได้เพิ่มสิ่งที่เหมือนกันหากในขณะที่เรามี
while (node.BranchData != null)
และ
if (node.BranchData != null)
ข้างในนั้น จากนั้นฉันก็ทำการวิเคราะห์ประสิทธิภาพกับสิ่งนั้นและใช้เวลานานกว่าหกเท่าในการดำเนินการเปรียบเทียบครั้งแรกเหมือนกับที่ดำเนินการเปรียบเทียบครั้งที่สองซึ่งจะคืนค่าจริงเสมอ ดูเหมือนว่าจะเป็นปัญหาการทำนายสาขา - และฉันเดาว่าไม่มีอะไรที่ฉันสามารถทำได้?!
แก้ไขอื่น
ผลลัพธ์ข้างต้นจะเกิดขึ้นเช่นกันหากต้องโหลด node.BranchData จาก RAM สำหรับการตรวจสอบ while - จากนั้นจะถูกแคชสำหรับคำสั่ง if
นี่เป็นคำถามที่สามของฉันในหัวข้อที่คล้ายกัน คราวนี้ฉันมุ่งเน้นไปที่โค้ดบรรทัดเดียว คำถามอื่น ๆ ของฉันเกี่ยวกับเรื่องนี้คือ:
BranchNode
คุณสมบัติnode.BranchData != null
ReferenceEquals(node.BranchData, null)
กรุณาพยายามที่จะเข้ามาแทนที่ มันสร้างความแตกต่างหรือไม่?