208 lines
6.5 KiB
C++
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
|