Tôi có một cây quyết định nhị phân quan trọng về hiệu suất và tôi muốn tập trung câu hỏi này vào một dòng mã. Dưới đây là mã cho trình lặp cây nhị phân với các kết quả từ việc chạy phân tích hiệu suất dựa trên nó.
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 là một trường, không phải một thuộc tính. Tôi đã làm điều này để ngăn chặn nguy cơ nó không được nội tuyến.
Lớp BranchNodeData như sau:
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;
}
Như bạn có thể thấy, kiểm tra vòng lặp while / null là một tác động lớn đến hiệu suất. Cái cây rất lớn, vì vậy tôi hy vọng việc tìm kiếm một chiếc lá sẽ mất một khoảng thời gian, nhưng tôi muốn hiểu khoảng thời gian không tương xứng dành cho một đường đó.
Tôi đã thử:
- Tách séc Null khỏi while - séc Null mới là hit.
- Thêm một trường boolean vào đối tượng và kiểm tra đối tượng đó, nó không có gì khác biệt. Điều gì được so sánh không quan trọng, chính sự so sánh mới là vấn đề.
Đây có phải là vấn đề dự đoán nhánh không? Nếu vậy, tôi có thể làm gì với nó? Nếu bất cứ điều gì?
Tôi sẽ không giả vờ hiểu CIL , nhưng tôi sẽ đăng nó cho bất kỳ ai hiểu được để họ có thể cố gắng thu thập một số thông tin từ nó.
.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
Chỉnh sửa: Tôi quyết định thực hiện kiểm tra dự đoán nhánh, tôi đã thêm một nếu giống hệt nhau nếu trong thời gian ngắn, vì vậy chúng tôi có
while (node.BranchData != null)
và
if (node.BranchData != null)
bên trong đó. Sau đó, tôi đã chạy phân tích hiệu suất so với điều đó và phải mất sáu lần lâu hơn để thực hiện phép so sánh đầu tiên như khi thực hiện phép so sánh thứ hai luôn trả về true. Vì vậy, có vẻ như nó thực sự là một vấn đề dự đoán nhánh - và tôi đoán rằng tôi không thể làm gì với nó ?!
Chỉnh sửa khác
Kết quả trên cũng sẽ xảy ra nếu node.BranchData phải được tải từ RAM trong quá trình kiểm tra trong khi - sau đó nó sẽ được lưu vào bộ nhớ đệm cho câu lệnh if.
Đây là câu hỏi thứ ba của tôi về một chủ đề tương tự. Lần này tôi đang tập trung vào một dòng mã. Các câu hỏi khác của tôi về chủ đề này là:
while(true) { /* current body */ if(node.BranchData == null) return node; }
. Nó có thay đổi gì không?
while(true) { BranchNodeData b = node.BranchData; if(ReferenceEquals(b, null)) return node; node = b.Child2; if (inputs[b.SplitInputIndex] <= b.SplitValue) node = b.Child1; }
Điều này sẽ node. BranchData
chỉ truy xuất một lần.
BranchNode
tài sản. Hãy thử thay thếnode.BranchData != null
ReferenceEquals(node.BranchData, null)
. Liệu nó có bất kỳ sự khác biệt?