Files
paddlepaddle--paddle/paddle/common/performance_statistician.h
T
2026-07-13 12:40:42 +08:00

208 lines
6.5 KiB
C++

// Copyright (c) 2024 CINN 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.
#pragma once
#include <chrono>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#if defined(PADDLE_WITH_CUDA)
#include <cuda.h>
#include <cuda_runtime.h>
#endif
#include "paddle/common/enforce.h"
namespace common {
using TimePoint = std::chrono::time_point<std::chrono::steady_clock>;
using TimeDuration = std::chrono::duration<double, std::micro>;
struct TimePointInfo {
bool is_start;
TimePoint time_point;
};
class PerformanceStatistician {
public:
using TimeRecordPerThread =
std::unordered_map<std::thread::id, std::vector<TimePointInfo>>;
static PerformanceStatistician& Instance() {
static PerformanceStatistician instance;
return instance;
}
void InsertTimePoint(const std::string& label, bool is_start) {
std::thread::id thread_id = std::this_thread::get_id();
if (is_start) {
std::lock_guard<std::mutex> lck_guard(record_mtx_);
TimePoint time_point = std::chrono::steady_clock::now();
record_[label][thread_id].push_back(TimePointInfo{is_start, time_point});
} else {
TimePoint time_point = std::chrono::steady_clock::now();
std::lock_guard<std::mutex> lck_guard(record_mtx_);
record_[label][thread_id].push_back(TimePointInfo{is_start, time_point});
}
}
void Start(const std::string& label) {
InsertTimePoint(label, /* is_start = */ true);
}
void End(const std::string& label) {
InsertTimePoint(label, /* is_start = */ false);
}
void CudaStart(const std::string& label) {
#if defined(PADDLE_WITH_CUDA)
std::lock_guard<std::mutex> lck_guard(record_mtx_);
cudaEvent_t e_start, e_stop;
cudaEventCreate(&e_start);
cudaEventCreate(&e_stop);
cuda_events_[label].first = e_start;
cuda_events_[label].second = e_stop;
cudaEventRecord(cuda_events_[label].first, 0);
#endif
}
void CudaEnd(const std::string& label) {
#if defined(PADDLE_WITH_CUDA)
std::lock_guard<std::mutex> lck_guard(record_mtx_);
PADDLE_ENFORCE_NE(cuda_events_.count(label),
0,
common::errors::InvalidArgument(
"Key for cuda time record does not exist"));
cudaEventRecord(cuda_events_[label].second, 0);
cudaEventSynchronize(cuda_events_[label].second);
float event_duration;
cudaEventElapsedTime(
&event_duration, cuda_events_[label].first, cuda_events_[label].second);
std::thread::id thread_id = std::this_thread::get_id();
TimePoint start_time_point = std::chrono::steady_clock::now();
TimePoint end_time_point =
start_time_point + std::chrono::nanoseconds(
static_cast<int64_t>(event_duration * 1000000));
record_[label][thread_id].push_back(TimePointInfo{true, start_time_point});
record_[label][thread_id].push_back(TimePointInfo{false, end_time_point});
cudaEventDestroy(cuda_events_[label].first);
cudaEventDestroy(cuda_events_[label].second);
cuda_events_.erase(label);
#endif
}
std::vector<TimePointInfo> Record(const std::string& label) const {
if (record_.count(label) == 0) return {};
std::vector<TimePointInfo> record_all_threads;
for (const auto& time_points : record_.at(label)) {
record_all_threads.insert(record_all_threads.end(),
time_points.second.begin(),
time_points.second.end());
}
return record_all_threads;
}
std::vector<TimePointInfo> RecordWithSubLabel(
const std::string& sub_label) const {
const auto IsSubStr = [](const std::string& sub, const std::string& str) {
return str.find(sub) != std::string::npos;
};
std::vector<TimePointInfo> records;
std::vector<std::string> labels = Labels();
for (const auto& label : labels) {
if (IsSubStr(sub_label, label)) {
auto appends = Record(label);
records.insert(records.end(), appends.begin(), appends.end());
}
}
return records;
}
std::vector<std::string> Labels() const {
std::vector<std::string> labels;
for (const auto& item : record_) {
labels.push_back(item.first);
}
return labels;
}
void Reset(const std::string& label) {
std::lock_guard<std::mutex> lck_guard(record_mtx_);
record_[label].clear();
}
void Reset() {
std::lock_guard<std::mutex> lck_guard(record_mtx_);
record_.clear();
}
void SetGraphNodesNum(int graph_nodes_num) {
graph_nodes_num_ = graph_nodes_num;
}
int GetGraphNodesNum() const { return graph_nodes_num_; }
private:
PerformanceStatistician() = default;
~PerformanceStatistician() = default;
PerformanceStatistician(const PerformanceStatistician&) = delete;
void operator=(const PerformanceStatistician&) = delete;
private:
std::unordered_map<std::string, TimeRecordPerThread> record_;
#if defined(PADDLE_WITH_CUDA)
std::unordered_map<std::string, std::pair<cudaEvent_t, cudaEvent_t>>
cuda_events_;
#endif
std::mutex record_mtx_;
int graph_nodes_num_ = 25;
};
class PerformanceReporter {
public:
static std::vector<TimeDuration> ExtractDuration(
const std::vector<TimePointInfo>& records,
bool contain_recursive = false);
static TimeDuration Sum(const std::vector<TimeDuration>& records);
static TimeDuration Mean(const std::vector<TimeDuration>& records);
static TimeDuration TrimMean(const std::vector<TimeDuration>& records);
static TimeDuration Max(const std::vector<TimeDuration>& records);
static TimeDuration Min(const std::vector<TimeDuration>& records);
static std::vector<TimeDuration> TopK(
const std::vector<TimeDuration>& records, int top_count);
static std::string Report(const std::vector<TimePointInfo>& records);
static std::string Report(const PerformanceStatistician& stat);
static void WriteToFile(const std::string& file, const std::string& report);
};
void PerformanceStatisticsStart(const std::string& label);
void PerformanceStatisticsEnd(const std::string& label);
} // namespace common