Files
paddlepaddle--paddle/paddle/fluid/platform/profiler/event_node.cc
T
2026-07-13 12:40:42 +08:00

449 lines
16 KiB
C++

/* Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/platform/profiler/event_node.h"
#include <climits>
#include <algorithm>
#include <deque>
#include <set>
#include <stack>
#include "paddle/phi/core/platform/profiler/utils.h"
namespace paddle::platform {
HostTraceEventNode::~HostTraceEventNode() {
// delete all runtime nodes and recursive delete children
for (auto& runtime_node_ptr : runtime_node_ptrs_) {
delete runtime_node_ptr;
}
for (auto& child : children_) {
delete child;
}
}
CudaRuntimeTraceEventNode::~CudaRuntimeTraceEventNode() {
// delete all device nodes
for (auto& device_node_ptr : device_node_ptrs_) {
delete device_node_ptr;
}
}
NodeTrees::~NodeTrees() {
// delete all root nodes
for (auto& item : thread_event_trees_map_) {
delete item.second;
}
}
void NodeTrees::BuildTrees(
const std::vector<HostTraceEventNode*>& host_event_nodes,
const std::vector<CudaRuntimeTraceEventNode*>& runtime_event_nodes,
const std::vector<DeviceTraceEventNode*>& device_event_nodes,
const std::vector<MemTraceEventNode*>& mem_event_nodes,
const std::vector<OperatorSupplementEventNode*>& op_supplement_events) {
// separate Host Event Nodes into different threads
std::map<uint64_t, std::vector<HostTraceEventNode*>>
thread2host_event_nodes; // used to store HostTraceEventNodes per thread
std::map<uint64_t, std::vector<CudaRuntimeTraceEventNode*>>
thread2runtime_event_nodes; // used to store CudaRuntimeTraceEventNode
// per
// thread
std::map<uint64_t, std::vector<MemTraceEventNode*>>
thread2mem_event_nodes; // used to store MemTraceEventNode
// per
// thread
std::map<uint64_t, std::vector<OperatorSupplementEventNode*>>
thread2op_supplement_event_nodes; // used to store
// OperatorSupplementEventNode
// per
// thread
std::map<uint32_t, CudaRuntimeTraceEventNode*>
correlation_id2runtime_event_node; // used to store the relation between
// correlation id and runtime node
// construct thread2host_event_nodes
for (auto host_event_node : host_event_nodes) {
thread2host_event_nodes[host_event_node->ThreadId()].push_back(
host_event_node);
}
// construct thread2runtime_event_nodes and
// correlation_id2runtime_event_node
for (auto runtime_event_node : runtime_event_nodes) {
thread2runtime_event_nodes[runtime_event_node->ThreadId()].push_back(
runtime_event_node);
correlation_id2runtime_event_node[runtime_event_node->CorrelationId()] =
runtime_event_node;
}
// associate CudaRuntimeTraceEventNode and DeviceTraceEventNode
// construct correlation_id2device_event_nodes
for (auto device_event_node : device_event_nodes) {
auto dst_iter = correlation_id2runtime_event_node.find(
device_event_node->CorrelationId());
if (dst_iter == correlation_id2runtime_event_node.end()) {
continue;
}
dst_iter->second->AddDeviceTraceEventNode(device_event_node);
}
// construct thread2mem_event_nodes
for (auto mem_event_node : mem_event_nodes) {
thread2mem_event_nodes[mem_event_node->ThreadId()].push_back(
mem_event_node);
}
// construct thread2op_supplement_event_nodes
for (auto op_supplement_event : op_supplement_events) {
thread2op_supplement_event_nodes[op_supplement_event->ThreadId()].push_back(
op_supplement_event);
}
// sort host event nodes and runtime event nodes according to start_ns and
// end_ns
// the smaller start_ns is, the further ahead position is.
// when start_ns of two nodes are equal, the one with bigger end_ns should be
// ahead.
for (auto& event_node : thread2host_event_nodes) {
std::sort(event_node.second.begin(),
event_node.second.end(),
[](HostTraceEventNode* node1, HostTraceEventNode* node2) {
if (node1->StartNs() < node2->StartNs()) {
return true;
}
if ((node1->StartNs() == node2->StartNs()) &&
(node1->EndNs() > node2->EndNs())) {
return true;
}
return false;
});
}
for (auto& event_node : thread2runtime_event_nodes) {
std::sort(
event_node.second.begin(),
event_node.second.end(),
[](CudaRuntimeTraceEventNode* node1, CudaRuntimeTraceEventNode* node2) {
if (node1->StartNs() < node2->StartNs()) {
return true;
}
if ((node1->StartNs() == node2->StartNs()) &&
(node1->EndNs() > node2->EndNs())) {
return true;
}
return false;
});
}
// sort mem event nodes and operator supplement event nodes
for (auto& event_node : thread2mem_event_nodes) {
std::sort(event_node.second.begin(),
event_node.second.end(),
[](MemTraceEventNode* node1, MemTraceEventNode* node2) {
if (node1->TimeStampNs() <= node2->TimeStampNs()) {
return true;
}
return false;
});
}
for (auto& event_node : thread2op_supplement_event_nodes) {
std::sort(event_node.second.begin(),
event_node.second.end(),
[](OperatorSupplementEventNode* node1,
OperatorSupplementEventNode* node2) {
if (node1->TimeStampNs() <= node2->TimeStampNs()) {
return true;
}
return false;
});
}
// construct trees
std::set<uint64_t> thread_set;
for (auto& event_node : thread2host_event_nodes) {
thread_set.insert(event_node.first);
}
for (auto& event_node : thread2runtime_event_nodes) {
thread_set.insert(event_node.first);
}
for (auto& event_node : thread2mem_event_nodes) {
thread_set.insert(event_node.first);
}
for (auto& event_node : thread2op_supplement_event_nodes) {
thread_set.insert(event_node.first);
}
for (auto item : thread_set) {
thread_event_trees_map_[item] =
BuildTreeRelationship(thread2host_event_nodes[item],
thread2runtime_event_nodes[item],
thread2mem_event_nodes[item],
thread2op_supplement_event_nodes[item]);
}
}
HostTraceEventNode* NodeTrees::BuildTreeRelationship(
std::vector<HostTraceEventNode*> host_event_nodes,
std::vector<CudaRuntimeTraceEventNode*> runtime_event_nodes,
std::vector<MemTraceEventNode*> mem_event_nodes,
std::vector<OperatorSupplementEventNode*> op_supplement_events) {
// a stack used for analyse relationship
auto node_stack = std::vector<HostTraceEventNode*>();
// root node, top level
auto root_node =
new HostTraceEventNode(HostTraceEvent(std::string("root node"),
TracerEventType::UserDefined,
0,
ULLONG_MAX,
0,
0));
// push root node into node_stack
node_stack.push_back(root_node);
// handle host_event_nodes
for (auto& host_event_node : host_event_nodes) {
while (true) {
auto stack_top_node = node_stack.back();
if (host_event_node->StartNs() < stack_top_node->EndNs()) {
// current node is the child of stack_top_node
PADDLE_ENFORCE_LE(
host_event_node->EndNs(),
stack_top_node->EndNs(),
common::errors::Fatal(
"should not have time range intersection within one thread"));
stack_top_node->AddChild(host_event_node);
node_stack.push_back(host_event_node);
break;
} else {
node_stack.pop_back();
// insert runtime node
// select runtime nodes which time range within stack_top_node
std::vector<CudaRuntimeTraceEventNode*>::iterator firstposition;
std::vector<CudaRuntimeTraceEventNode*>::iterator lastposition =
runtime_event_nodes.end();
bool hasenter = false;
for (auto runtimenode = runtime_event_nodes.begin();
runtimenode != runtime_event_nodes.end();
++runtimenode) {
if (((*runtimenode)->StartNs() >= stack_top_node->StartNs()) &&
((*runtimenode)->EndNs() <= stack_top_node->EndNs())) {
if (!hasenter) {
firstposition = runtimenode;
hasenter = true;
}
stack_top_node->AddCudaRuntimeNode(*runtimenode);
} else {
// from this runtime node, not within stack_top_node, erase the
// nodes from runtime_event_nodes
if ((*runtimenode)->StartNs() > stack_top_node->EndNs()) {
lastposition = runtimenode;
break;
}
}
}
if (hasenter) {
runtime_event_nodes.erase(firstposition, lastposition);
}
}
}
}
// to insert left runtimenode into host_event_nodes
while (!node_stack.empty()) {
auto stack_top_node = node_stack.back();
// insert runtime node
// select runtime nodes which time range within stack_top_node
std::vector<CudaRuntimeTraceEventNode*>::iterator firstposition;
std::vector<CudaRuntimeTraceEventNode*>::iterator lastposition =
runtime_event_nodes.end();
bool hasenter = false;
for (auto runtimenode = runtime_event_nodes.begin();
runtimenode != runtime_event_nodes.end();
++runtimenode) {
if (((*runtimenode)->StartNs() >= stack_top_node->StartNs()) &&
((*runtimenode)->EndNs() <= stack_top_node->EndNs())) {
if (!hasenter) {
firstposition = runtimenode;
hasenter = true;
}
stack_top_node->AddCudaRuntimeNode(*runtimenode);
} else {
// from this runtime node, not within stack_top_node, erase the
// nodes from runtime_event_nodes
if ((*runtimenode)->StartNs() > stack_top_node->EndNs()) {
lastposition = runtimenode;
break;
}
}
}
if (hasenter) {
runtime_event_nodes.erase(firstposition, lastposition);
}
node_stack.pop_back();
}
// build relationship between host event node and mem event node
// First, post-order traverse the tree. Then, insert the memory and op
// supplement node into correct host nodes.
auto stack = std::stack<HostTraceEventNode*>();
auto flag_stack = std::stack<int32_t>();
auto post_order_nodes = std::vector<HostTraceEventNode*>();
stack.push(root_node);
flag_stack.push(0);
while (!stack.empty()) {
auto current_node = stack.top();
stack.pop();
auto flag = flag_stack.top();
flag_stack.pop();
if (flag == 0) {
stack.push(current_node);
flag_stack.push(1);
for (auto child = current_node->GetChildren().rbegin();
child != current_node->GetChildren().rend();
++child) {
stack.push(*child);
flag_stack.push(0);
}
} else {
post_order_nodes.push_back(current_node);
}
}
for (auto it = post_order_nodes.begin(); it < post_order_nodes.end(); ++it) {
bool hasenter = false;
std::vector<MemTraceEventNode*>::iterator firstposition;
std::vector<MemTraceEventNode*>::iterator lastposition =
mem_event_nodes.end();
for (auto mem_it = mem_event_nodes.begin(); mem_it < mem_event_nodes.end();
++mem_it) {
if ((*mem_it)->TimeStampNs() >= (*it)->StartNs() &&
(*mem_it)->TimeStampNs() <= (*it)->EndNs()) {
(*it)->AddMemNode(*mem_it);
if (!hasenter) {
firstposition = mem_it;
hasenter = true;
}
} else {
if ((*mem_it)->TimeStampNs() > (*it)->EndNs()) {
lastposition = mem_it;
break;
}
}
}
if (hasenter) {
mem_event_nodes.erase(firstposition, lastposition);
}
}
// build relationship between host event node and op supplement node
for (auto it = post_order_nodes.begin(); it < post_order_nodes.end(); ++it) {
bool hasenter = false;
std::vector<OperatorSupplementEventNode*>::iterator firstposition;
std::vector<OperatorSupplementEventNode*>::iterator lastposition =
op_supplement_events.end();
for (auto op_supplement_it = op_supplement_events.begin();
op_supplement_it < op_supplement_events.end();
++op_supplement_it) {
if ((*op_supplement_it)->TimeStampNs() >= (*it)->StartNs() &&
(*op_supplement_it)->TimeStampNs() <= (*it)->EndNs()) {
if (!hasenter) {
firstposition = op_supplement_it;
hasenter = true;
}
(*it)->SetOperatorSupplementNode(*op_supplement_it);
} else {
if ((*op_supplement_it)->TimeStampNs() > (*it)->EndNs()) {
lastposition = op_supplement_it;
break;
}
}
}
if (hasenter) {
op_supplement_events.erase(firstposition, lastposition);
}
}
return root_node;
}
std::map<uint64_t, std::vector<HostTraceEventNode*>> NodeTrees::Traverse(
bool bfs) const {
// traverse the tree, provide two methods: bfs(breadth first search) or
// dfs(depth first search)
std::map<uint64_t, std::vector<HostTraceEventNode*>> thread2host_event_nodes;
if (bfs == true) {
for (auto item : thread_event_trees_map_) {
auto deque = std::deque<HostTraceEventNode*>();
uint64_t thread_id = item.first;
auto root_node = item.second;
deque.push_back(root_node);
while (!deque.empty()) {
auto current_node = deque.front();
deque.pop_front();
thread2host_event_nodes[thread_id].push_back(current_node);
for (auto child : current_node->GetChildren()) {
deque.push_back(child);
}
}
}
} else {
for (auto item : thread_event_trees_map_) {
auto stack = std::stack<HostTraceEventNode*>();
uint64_t thread_id = item.first;
auto root_node = item.second;
stack.push(root_node);
while (!stack.empty()) {
auto current_node = stack.top();
stack.pop();
thread2host_event_nodes[thread_id].push_back(current_node);
for (auto child = current_node->GetChildren().rbegin();
child != current_node->GetChildren().rend();
++child) {
stack.push(*child);
}
}
}
}
return thread2host_event_nodes;
}
void NodeTrees::LogMe(BaseLogger* logger) { logger->LogNodeTrees(*this); }
void NodeTrees::HandleTrees(
std::function<void(HostTraceEventNode*)> host_event_node_handle,
std::function<void(CudaRuntimeTraceEventNode*)> runtime_event_node_handle,
std::function<void(DeviceTraceEventNode*)> device_event_node_handle,
std::function<void(MemTraceEventNode*)> mem_event_node_handle,
std::function<void(OperatorSupplementEventNode*)>
op_supplement_node_handle) {
// using different user-defined function to handle different nodes
const std::map<uint64_t, std::vector<HostTraceEventNode*>>
thread2host_event_nodes = Traverse(true);
for (const auto& event_node : thread2host_event_nodes) {
for (auto hostnode = event_node.second.begin();
hostnode != event_node.second.end();
++hostnode) {
if (hostnode != event_node.second.begin()) { // skip root node
host_event_node_handle(*hostnode);
}
for (auto event_node : (*hostnode)->GetRuntimeTraceEventNodes()) {
runtime_event_node_handle(event_node);
for (auto devicenode : event_node->GetDeviceTraceEventNodes()) {
device_event_node_handle(devicenode);
}
}
for (auto event_node : (*hostnode)->GetMemTraceEventNodes()) {
mem_event_node_handle(event_node);
}
if ((*hostnode)->GetOperatorSupplementEventNode()) {
op_supplement_node_handle(
(*hostnode)->GetOperatorSupplementEventNode());
}
}
}
}
} // namespace paddle::platform