开源代码评注赛:胡一菲最nb队PR #13
|
|
@ -24,13 +24,23 @@
|
|||
namespace mindspore {
|
||||
namespace profiler {
|
||||
namespace cpu {
|
||||
/**
|
||||
* @brief Static instance of CpuDataSaver used for accessing CPU profiling data saver functionality.
|
||||
*/
|
||||
std::shared_ptr<CpuDataSaver> CpuDataSaver::cpu_data_saver_inst_ = std::make_shared<CpuDataSaver>();
|
||||
|
||||
/**
|
||||
* @brief Write profiling data to a file at the specified output path directory.
|
||||
*
|
||||
* @param out_path_dir The directory path where profiling data will be written.
|
||||
*/
|
||||
void CpuDataSaver::WriteFile(const std::string out_path_dir) {
|
||||
if (op_detail_infos_.empty() || op_type_infos_.empty()) {
|
||||
MS_LOG(INFO) << "No cpu operation detail infos to write.";
|
||||
MS_LOG(INFO) << "No CPU operation detail infos to write.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine device ID based on context or environment variables.
|
||||
#if ENABLE_GPU
|
||||
auto context_ptr = MsContext::GetInstance();
|
||||
MS_EXCEPTION_IF_NULL(context_ptr);
|
||||
|
|
@ -38,11 +48,14 @@ void CpuDataSaver::WriteFile(const std::string out_path_dir) {
|
|||
device_id_ = std::to_string(device_id);
|
||||
#else
|
||||
auto rank_id = common::GetEnv("RANK_ID");
|
||||
|
||||
// If RANK_ID is not set, default value is 0.
|
||||
if (rank_id.empty()) {
|
||||
rank_id = "0";
|
||||
}
|
||||
|
||||
rank_id = std::string(rank_id);
|
||||
|
||||
// When the value of RANK_ID is not a number, set its value to 0.
|
||||
for (int i = 0; i < static_cast<int>(rank_id.size()); i++) {
|
||||
if (std::isdigit(rank_id[i]) == 0) {
|
||||
|
|
@ -50,16 +63,28 @@ void CpuDataSaver::WriteFile(const std::string out_path_dir) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
device_id_ = rank_id;
|
||||
#endif
|
||||
|
||||
op_side_ = "cpu";
|
||||
WriteOpDetail(out_path_dir);
|
||||
WriteOpType(out_path_dir);
|
||||
WriteOpTimestamp(out_path_dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a reference to OpTimestampInfo, which stores operation timestamp information.
|
||||
*
|
||||
* @return A reference to OpTimestampInfo.
|
||||
*/
|
||||
OpTimestampInfo &CpuDataSaver::GetOpTimeStampInfo() { return op_timestamps_map_; }
|
||||
|
||||
/**
|
||||
* @brief Get the shared instance of CpuDataSaver for accessing CPU profiling data saver functionality.
|
||||
*
|
||||
* @return A reference to the shared instance of CpuDataSaver.
|
||||
*/
|
||||
std::shared_ptr<CpuDataSaver> &CpuDataSaver::GetInstance() { return cpu_data_saver_inst_; }
|
||||
} // namespace cpu
|
||||
} // namespace profiler
|
||||
|
|
|
|||
|
|
@ -35,13 +35,29 @@ class CpuDataSaver : public DataSaver {
|
|||
|
||||
~CpuDataSaver() = default;
|
||||
|
||||
CpuDataSaver(const CpuDataSaver &) = delete;
|
||||
/**
|
||||
* @brief Copy constructor is deleted to prevent copying of CpuDataSaver instances.
|
||||
*/
|
||||
CpuDataSaver(const CpuDataSaver &) = delete;
|
||||
|
||||
CpuDataSaver &operator=(const CpuDataSaver &) = delete;
|
||||
/**
|
||||
* @brief Assignment operator is deleted to prevent assignment of CpuDataSaver instances.
|
||||
*/
|
||||
CpuDataSaver &operator=(const CpuDataSaver &) = delete;
|
||||
|
||||
OpTimestampInfo &GetOpTimeStampInfo();
|
||||
/**
|
||||
* @brief Get a reference to OpTimestampInfo, which stores operation timestamp information.
|
||||
*
|
||||
* @return A reference to OpTimestampInfo.
|
||||
*/
|
||||
OpTimestampInfo &GetOpTimeStampInfo();
|
||||
|
||||
void WriteFile(const std::string out_path);
|
||||
/**
|
||||
* @brief Write profiling data to a file at the specified output path.
|
||||
*
|
||||
* @param out_path The path to the output file where profiling data will be written.
|
||||
*/
|
||||
void WriteFile(const std::string out_path);
|
||||
|
||||
private:
|
||||
static std::shared_ptr<CpuDataSaver> cpu_data_saver_inst_;
|
||||
|
|
|
|||
|
|
@ -28,60 +28,73 @@
|
|||
namespace mindspore {
|
||||
namespace profiler {
|
||||
namespace cpu {
|
||||
|
||||
// CPUProfiler class instance
|
||||
std::shared_ptr<CPUProfiler> CPUProfiler::profiler_inst_ = std::make_shared<CPUProfiler>();
|
||||
|
||||
// Get an instance of the CPUProfiler class
|
||||
std::shared_ptr<CPUProfiler> &CPUProfiler::GetInstance() { return profiler_inst_; }
|
||||
|
||||
// Initialize the CPUProfiler
|
||||
void CPUProfiler::Init(const std::string &profileDataPath = "") {
|
||||
// Initialize the CPUProfiler
|
||||
MS_LOG(INFO) << "Initialize CPU Profiling";
|
||||
base_time_ = GetHostMonoTimeStamp();
|
||||
profile_data_path_ = profileDataPath;
|
||||
base_time_ = GetHostMonoTimeStamp(); // Get the host's monotonically increasing timestamp
|
||||
profile_data_path_ = profileDataPath; // Set the path to store profiling data
|
||||
MS_LOG(INFO) << " Host start time(ns): " << base_time_ << " profile data path: " << profile_data_path_;
|
||||
}
|
||||
|
||||
// Enable or disable step profiling
|
||||
void CPUProfiler::StepProfilingEnable(const bool enable_flag) {
|
||||
// Enable or disable step profiling
|
||||
MS_LOG(INFO) << "CPU Profiler enable flag: " << enable_flag;
|
||||
enable_flag_ = enable_flag;
|
||||
}
|
||||
|
||||
// Set runtime data for an operation
|
||||
void CPUProfiler::SetRunTimeData(const std::string &op_name, const uint32_t pid, bool is_parallel) {
|
||||
// Set runtime data for an operation
|
||||
if (!is_parallel) {
|
||||
op_name_ = op_name;
|
||||
pid_ = pid;
|
||||
op_name_ = op_name; // Set the current operation's name
|
||||
pid_ = pid; // Set the process ID of the current operation
|
||||
}
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(op_map_mutex_);
|
||||
auto iter = op_info_map_.find(op_name);
|
||||
if (iter != op_info_map_.end()) {
|
||||
iter->second.op_count += 1;
|
||||
iter->second.op_count += 1; // Increment operation count if the operation already exists
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::unique_lock<std::shared_mutex> lock(op_map_mutex_);
|
||||
OpInfo op_info;
|
||||
op_info.op_name = op_name;
|
||||
op_info.pid = pid;
|
||||
op_info.op_count = 1;
|
||||
op_info_map_[op_name] = op_info;
|
||||
op_info.op_name = op_name; // Set the operation's name
|
||||
op_info.pid = pid; // Set the process ID of the operation
|
||||
op_info.op_count = 1; // Initialize the operation count
|
||||
op_info_map_[op_name] = op_info; // Add the operation to the map
|
||||
}
|
||||
|
||||
// Set the start time for an operation
|
||||
void CPUProfiler::SetRuntimeStart(const std::string op_name, const uint64_t start_timestamp) {
|
||||
// Set the start time for an operation
|
||||
std::shared_lock<std::shared_mutex> lock(op_map_mutex_);
|
||||
auto iter = op_info_map_.find(op_name);
|
||||
if (iter != op_info_map_.end()) {
|
||||
iter->second.tmp_start_duration.start_timestamp = start_timestamp;
|
||||
iter->second.tmp_start_duration.start_timestamp = start_timestamp; // Set the start timestamp
|
||||
auto actor_manager = ActorMgr::GetActorMgrRef();
|
||||
MS_EXCEPTION_IF_NULL(actor_manager);
|
||||
auto thread_pool = actor_manager->GetActorThreadPool();
|
||||
auto worker_ids_map = thread_pool->GetWorkerIdMap();
|
||||
auto id_iter = worker_ids_map.find(std::this_thread::get_id());
|
||||
if (id_iter != worker_ids_map.end()) {
|
||||
iter->second.tmp_start_duration.tid = id_iter->second;
|
||||
iter->second.tmp_start_duration.tid = id_iter->second; // Set the thread ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the end time for an operation and return elapsed time
|
||||
float CPUProfiler::SetRuntimeEnd(const std::string op_name, const uint64_t stop_timestamp) {
|
||||
// Set the end time for an operation and return the elapsed time
|
||||
float op_time_elapsed = 0;
|
||||
std::shared_lock<std::shared_mutex> lock(op_map_mutex_);
|
||||
auto iter = op_info_map_.find(op_name);
|
||||
|
|
@ -98,22 +111,23 @@ float CPUProfiler::SetRuntimeEnd(const std::string op_name, const uint64_t stop_
|
|||
MS_LOG(EXCEPTION) << "Op " << op_name << " start time thread id must be equal to end thread id.";
|
||||
}
|
||||
}
|
||||
(void)iter->second.start_duration.emplace_back(iter->second.tmp_start_duration);
|
||||
(void)iter->second.start_duration.emplace_back(iter->second.tmp_start_duration); // Store duration info
|
||||
op_time_elapsed = iter->second.tmp_start_duration.duration;
|
||||
}
|
||||
return op_time_elapsed;
|
||||
}
|
||||
|
||||
// Begin producing operation data for parallel profiling
|
||||
void CPUProfiler::OpDataProducerBeginParallel(const std::string op_name, const uint32_t pid) {
|
||||
auto start_timestamp = GetHostMonoTimeStamp();
|
||||
SetRunTimeData(op_name, pid, true);
|
||||
SetRunTimeData(op_name, pid, true); // Set runtime data for parallel operation
|
||||
SetRuntimeStart(op_name, start_timestamp);
|
||||
|
||||
#if ENABLE_GPU
|
||||
if (MsContext::GetInstance()->get_param<bool>(MS_CTX_ENABLE_MINDRT)) {
|
||||
// For heterogeneous scene, record op name to gpu_profiler_inst.
|
||||
auto gpu_profiler_inst = profiler::gpu::GPUProfiler::GetInstance();
|
||||
// For cpu network, no gpu profiler, do not to raise exception.
|
||||
// For cpu network, no gpu profiler, do not raise an exception.
|
||||
if (gpu_profiler_inst && gpu_profiler_inst->GetEnableFlag()) {
|
||||
gpu_profiler_inst->RecordOneStepStartEndInfo(op_name);
|
||||
}
|
||||
|
|
@ -121,6 +135,7 @@ void CPUProfiler::OpDataProducerBeginParallel(const std::string op_name, const u
|
|||
#endif
|
||||
}
|
||||
|
||||
// End producing operation data for parallel profiling
|
||||
void CPUProfiler::OpDataProducerEndParallel(const std::string op_name) {
|
||||
auto stop_timestamp = GetHostMonoTimeStamp();
|
||||
float op_time_elapsed = SetRuntimeEnd(op_name, stop_timestamp);
|
||||
|
|
@ -128,6 +143,7 @@ void CPUProfiler::OpDataProducerEndParallel(const std::string op_name) {
|
|||
Profiler::SetRunTimeData(op_name, op_time_elapsed);
|
||||
}
|
||||
|
||||
// Begin producing operation data for profiling
|
||||
void CPUProfiler::OpDataProducerBegin(const std::string op_name, const uint32_t pid) {
|
||||
op_time_start_ = GetHostMonoTimeStamp();
|
||||
op_time_mono_start_ = GetHostMonoTimeStamp();
|
||||
|
|
@ -137,7 +153,7 @@ void CPUProfiler::OpDataProducerBegin(const std::string op_name, const uint32_t
|
|||
if (MsContext::GetInstance()->get_param<bool>(MS_CTX_ENABLE_MINDRT)) {
|
||||
// For heterogeneous scene, record op name to gpu_profiler_inst.
|
||||
auto gpu_profiler_inst = profiler::gpu::GPUProfiler::GetInstance();
|
||||
// For cpu network, no gpu profiler, do not to raise exception.
|
||||
// For cpu network, no gpu profiler, do not raise an exception.
|
||||
if (gpu_profiler_inst && gpu_profiler_inst->GetEnableFlag()) {
|
||||
gpu_profiler_inst->RecordOneStepStartEndInfo(op_name);
|
||||
}
|
||||
|
|
@ -145,6 +161,7 @@ void CPUProfiler::OpDataProducerBegin(const std::string op_name, const uint32_t
|
|||
#endif
|
||||
}
|
||||
|
||||
// End producing operation data for profiling
|
||||
void CPUProfiler::OpDataProducerEnd() {
|
||||
float op_time_elapsed = 0;
|
||||
op_time_stop_ = GetHostMonoTimeStamp();
|
||||
|
|
@ -154,33 +171,39 @@ void CPUProfiler::OpDataProducerEnd() {
|
|||
Profiler::SetRunTimeData(op_name_, op_time_mono_start_, op_time_elapsed);
|
||||
}
|
||||
|
||||
// Stop CPU profiling
|
||||
void CPUProfiler::Stop() {
|
||||
MS_LOG(INFO) << "Stop CPU Profiling";
|
||||
SaveProfileData();
|
||||
ClearInst();
|
||||
}
|
||||
|
||||
// Save the profiling data to a file
|
||||
void CPUProfiler::SaveProfileData() {
|
||||
if (profile_data_path_.empty()) {
|
||||
MS_LOG(WARNING) << "Profile data path is empty, skip save profile data.";
|
||||
MS_LOG(WARNING) << "Profile data path is empty, skip saving profile data.";
|
||||
} else {
|
||||
auto cpu_data_saver_inst = profiler::cpu::CpuDataSaver::GetInstance();
|
||||
MS_EXCEPTION_IF_NULL(cpu_data_saver_inst);
|
||||
cpu_data_saver_inst->ParseOpInfo(op_info_map_);
|
||||
cpu_data_saver_inst->WriteFile(profile_data_path_);
|
||||
cpu_data_saver_inst->ParseOpInfo(op_info_map_); // Parse operation info
|
||||
cpu_data_saver_inst->WriteFile(profile_data_path_); // Write data to the specified file
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the CPU profiler instance
|
||||
void CPUProfiler::ClearInst() { op_info_map_.clear(); }
|
||||
|
||||
// Register CPUProfiler class in Python binding
|
||||
REGISTER_PYBIND_DEFINE(CPUProfiler_, ([](const py::module *m) {
|
||||
(void)py::class_<CPUProfiler, std::shared_ptr<CPUProfiler>>(*m, "CPUProfiler")
|
||||
.def_static("get_instance", &CPUProfiler::GetInstance, "CPUProfiler get_instance.")
|
||||
.def("init", &CPUProfiler::Init, py::arg("profile_data_path"), "init")
|
||||
.def("stop", &CPUProfiler::Stop, "stop")
|
||||
.def("init", &CPUProfiler::Init, py::arg("profile_data_path"), "Initialize CPU profiler")
|
||||
.def("stop", &CPUProfiler::Stop, "Stop CPU profiler")
|
||||
.def("step_profiling_enable", &CPUProfiler::StepProfilingEnable, py::arg("enable_flag"),
|
||||
"enable or disable step profiling");
|
||||
"Enable or disable step profiling");
|
||||
}));
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace profiler
|
||||
} // namespace mindspore
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,10 @@
|
|||
namespace mindspore {
|
||||
namespace profiler {
|
||||
namespace cpu {
|
||||
// Conversion factor from nanoseconds to milliseconds
|
||||
const float kNanosecondToMillisecond = 1000000;
|
||||
|
||||
// CPUProfiler class for CPU profiling
|
||||
class CPUProfiler : public Profiler {
|
||||
public:
|
||||
static std::shared_ptr<CPUProfiler> &GetInstance();
|
||||
|
|
@ -42,14 +44,69 @@ class CPUProfiler : public Profiler {
|
|||
CPUProfiler(const CPUProfiler &) = delete;
|
||||
CPUProfiler &operator=(const CPUProfiler &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Initialize the CPU profiler.
|
||||
*
|
||||
* @param profileDataPath The path to store the profiling data.
|
||||
*/
|
||||
void Init(const std::string &profileDataPath) override;
|
||||
|
||||
/**
|
||||
* @brief Stop the CPU profiler.
|
||||
*/
|
||||
void Stop() override;
|
||||
|
||||
/**
|
||||
* @brief Enable or disable step profiling.
|
||||
*
|
||||
* @param enable_flag True to enable step profiling, false to disable.
|
||||
*/
|
||||
void StepProfilingEnable(const bool enable_flag) override;
|
||||
|
||||
/**
|
||||
* @brief Begin producing operation data for profiling.
|
||||
*
|
||||
* @param op_name The name of the operation.
|
||||
* @param pid The process ID associated with the operation.
|
||||
*/
|
||||
void OpDataProducerBegin(const std::string op_name, const uint32_t pid);
|
||||
|
||||
/**
|
||||
* @brief End producing operation data for profiling.
|
||||
*/
|
||||
void OpDataProducerEnd() override;
|
||||
|
||||
/**
|
||||
* @brief End producing operation data for parallel profiling.
|
||||
*
|
||||
* @param op_name The name of the operation.
|
||||
*/
|
||||
void OpDataProducerEndParallel(const std::string op_name);
|
||||
|
||||
/**
|
||||
* @brief Begin producing operation data for parallel profiling.
|
||||
*
|
||||
* @param op_name The name of the operation.
|
||||
* @param pid The process ID associated with the operation.
|
||||
*/
|
||||
void OpDataProducerBeginParallel(const std::string op_name, const uint32_t pid);
|
||||
|
||||
/**
|
||||
* @brief Set the end time for an operation.
|
||||
*
|
||||
* @param op_name The name of the operation.
|
||||
* @param stop_timestamp The timestamp when the operation ended.
|
||||
*
|
||||
* @return The elapsed time for the operation.
|
||||
*/
|
||||
float SetRuntimeEnd(const std::string op_name, const uint64_t stop_timestamp);
|
||||
|
||||
/**
|
||||
* @brief Set the start time for an operation.
|
||||
*
|
||||
* @param op_name The name of the operation.
|
||||
* @param start_timestamp The timestamp when the operation started.
|
||||
*/
|
||||
void SetRuntimeStart(const std::string op_name, const uint64_t start_timestamp);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@
|
|||
|
||||
namespace mindspore {
|
||||
namespace profiler {
|
||||
// Constructor for OpDetailInfo
|
||||
OpDetailInfo::OpDetailInfo(const std::shared_ptr<OpInfo> op_info, float proportion)
|
||||
: op_info_(op_info), proportion_(proportion) {
|
||||
// op_full_name is like 'xxx/xxx/{op_type}-op{node_id}'
|
||||
// Extract information from op_full_name, which is like 'xxx/xxx/{op_type}-op{node_id}'
|
||||
op_full_name_ = op_info->op_name;
|
||||
auto op_type_begin_iter = op_full_name_.rfind('/') + 1;
|
||||
auto op_type_end_iter = op_full_name_.rfind('-');
|
||||
|
|
@ -37,6 +38,7 @@ OpDetailInfo::OpDetailInfo(const std::shared_ptr<OpInfo> op_info, float proporti
|
|||
op_avg_time_ = op_info->op_host_cost_time / op_info->op_count;
|
||||
}
|
||||
|
||||
// Parses information about operations and timestamps
|
||||
void DataSaver::ParseOpInfo(const OpInfoMap &op_info_maps) {
|
||||
op_detail_infos_.reserve(op_info_maps.size());
|
||||
float total_time_sum = GetTotalOpTime(op_info_maps);
|
||||
|
|
@ -46,19 +48,21 @@ void DataSaver::ParseOpInfo(const OpInfoMap &op_info_maps) {
|
|||
MS_LOG(ERROR) << "The total operation times can not be 0.";
|
||||
return;
|
||||
}
|
||||
// Calculate the proportion of this operation's time relative to the total time
|
||||
float proportion = item.second.op_host_cost_time / total_time_sum;
|
||||
auto op_info = std::make_shared<OpInfo>(item.second);
|
||||
if (op_info == nullptr) {
|
||||
MS_LOG(ERROR) << "Create Operation information node failed when parse operation information.";
|
||||
return;
|
||||
}
|
||||
// Create OpDetailInfo object to store detailed information about this operation
|
||||
OpDetailInfo op_detail_info = OpDetailInfo(op_info, proportion);
|
||||
op_detail_infos_.emplace_back(op_detail_info);
|
||||
// Add detailed information to the corresponding operation type
|
||||
AddOpDetailInfoForType(op_detail_info);
|
||||
}
|
||||
// update average time of op type
|
||||
// Update average time of each operation type
|
||||
for (auto &op_type : op_type_infos_) {
|
||||
// device_infos: <type_name, op_type_info>
|
||||
if (op_type.second.count_ == 0) {
|
||||
MS_LOG(ERROR) << "The num of operation type can not be 0.";
|
||||
return;
|
||||
|
|
@ -69,6 +73,7 @@ void DataSaver::ParseOpInfo(const OpInfoMap &op_info_maps) {
|
|||
MS_LOG(DEBUG) << "Get " << op_type_infos_.size() << " operation type items.";
|
||||
}
|
||||
|
||||
// Adds detailed information about an operation type to op_type_infos_
|
||||
void DataSaver::AddOpDetailInfoForType(const OpDetailInfo &op_detail_info) {
|
||||
// Construct OpType object according to op detail info
|
||||
OpType op_type = OpType{op_detail_info.op_type_,
|
||||
|
|
@ -87,24 +92,27 @@ void DataSaver::AddOpDetailInfoForType(const OpDetailInfo &op_detail_info) {
|
|||
}
|
||||
}
|
||||
|
||||
// Calculates the total time spent on all operations in op_info_maps
|
||||
float DataSaver::GetTotalOpTime(const OpInfoMap &op_info_maps) const {
|
||||
float sum = 0;
|
||||
// Calculate the sum of op_host_cost_time for all operations
|
||||
sum = std::accumulate(op_info_maps.begin(), op_info_maps.end(), sum,
|
||||
[](float i, auto iter) { return i + iter.second.op_host_cost_time; });
|
||||
MS_LOG(DEBUG) << "The total op time is " << sum;
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Writes information about operation types to a CSV file
|
||||
void DataSaver::WriteOpType(const std::string &saver_base_dir) const {
|
||||
std::string file_path = saver_base_dir + "/" + op_side_ + "_op_type_info_" + device_id_ + ".csv";
|
||||
std::ofstream ofs(file_path);
|
||||
// check if the file is writable
|
||||
// Check if the file is writable
|
||||
if (!ofs.is_open()) {
|
||||
MS_LOG(WARNING) << "Open file '" << file_path << "' failed!";
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// write op type info into file
|
||||
// Write op type info into file
|
||||
if (op_side_ == "cpu") {
|
||||
ofs << OpType().GetCpuHeader() << std::endl;
|
||||
for (auto op_type_info : op_type_infos_) {
|
||||
|
|
@ -118,13 +126,14 @@ void DataSaver::WriteOpType(const std::string &saver_base_dir) const {
|
|||
}
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
MS_LOG(ERROR) << "Write " << file_path << "failed: " << e.what();
|
||||
MS_LOG(ERROR) << "Write " << file_path << " failed: " << e.what();
|
||||
}
|
||||
ofs.close();
|
||||
ChangeFileMode(file_path);
|
||||
MS_LOG(INFO) << "Write " << op_type_infos_.size() << " op type infos into file: " << file_path;
|
||||
}
|
||||
|
||||
// Writes detailed information about operations to a CSV file
|
||||
void DataSaver::WriteOpDetail(const std::string &saver_base_dir) const {
|
||||
std::string file_path = saver_base_dir + "/" + op_side_ + "_op_detail_info_" + device_id_ + ".csv";
|
||||
std::ofstream ofs(file_path);
|
||||
|
|
@ -133,7 +142,7 @@ void DataSaver::WriteOpDetail(const std::string &saver_base_dir) const {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
// write op detail info into file
|
||||
// Write op detail info into file
|
||||
if (op_side_ == "cpu") {
|
||||
ofs << OpDetailInfo().GetCpuHeader() << std::endl;
|
||||
for (auto op_detail : op_detail_infos_) {
|
||||
|
|
@ -147,23 +156,24 @@ void DataSaver::WriteOpDetail(const std::string &saver_base_dir) const {
|
|||
}
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
MS_LOG(ERROR) << "Write " << file_path << "failed: " << e.what();
|
||||
MS_LOG(ERROR) << "Write " << file_path << " failed: " << e.what();
|
||||
}
|
||||
ofs.close();
|
||||
ChangeFileMode(file_path);
|
||||
MS_LOG(INFO) << "Write " << op_detail_infos_.size() << " op detail infos into file: " << file_path;
|
||||
}
|
||||
|
||||
// Writes timestamps of operation executions to a text file
|
||||
void DataSaver::WriteOpTimestamp(const std::string &saver_base_dir) const {
|
||||
std::string file_path = saver_base_dir + "/" + op_side_ + "_op_execute_timestamp_" + device_id_ + ".txt";
|
||||
std::ofstream ofs(file_path);
|
||||
// check if the file is writable
|
||||
// Check if the file is writable
|
||||
if (!ofs.is_open()) {
|
||||
MS_LOG(WARNING) << "Open file '" << file_path << "' failed!";
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// write op timestamp info into file
|
||||
// Write op timestamp info into file
|
||||
for (const auto &op_timestamp_info : op_timestamps_map_) {
|
||||
if (op_side_ == "cpu") {
|
||||
ofs << op_timestamp_info.first << ";HostCpuOps;";
|
||||
|
|
@ -179,17 +189,19 @@ void DataSaver::WriteOpTimestamp(const std::string &saver_base_dir) const {
|
|||
ofs << std::endl;
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
MS_LOG(ERROR) << "Write " << file_path << "failed: " << e.what();
|
||||
MS_LOG(ERROR) << "Write " << file_path << " failed: " << e.what();
|
||||
}
|
||||
ofs.close();
|
||||
ChangeFileMode(file_path);
|
||||
}
|
||||
|
||||
// Changes the file mode of the specified file to read-write
|
||||
void DataSaver::ChangeFileMode(const std::string &file_path) const {
|
||||
if (chmod(common::SafeCStr(file_path), S_IRUSR | S_IWUSR) == -1) {
|
||||
MS_LOG(WARNING) << "Modify file: " << file_path << " to rw fail.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace mindspore
|
||||
|
|
|
|||
|
|
@ -26,87 +26,52 @@
|
|||
#include "utils/log_adapter.h"
|
||||
namespace mindspore {
|
||||
namespace profiler {
|
||||
// Structure to store detailed information about an operation
|
||||
struct OpDetailInfo {
|
||||
std::string op_type_;
|
||||
std::string op_name_;
|
||||
std::string op_full_name_;
|
||||
std::shared_ptr<OpInfo> op_info_{nullptr};
|
||||
float op_avg_time_{0};
|
||||
float proportion_{0};
|
||||
std::string op_type_; /**< Type of the operation. */
|
||||
std::string op_name_; /**< Name of the operation. */
|
||||
std::string op_full_name_; /**< Full name of the operation. */
|
||||
std::shared_ptr<OpInfo> op_info_{nullptr}; /**< Shared pointer to information about the operation. */
|
||||
float op_avg_time_{0}; /**< Average time for the operation. */
|
||||
float proportion_{0}; /**< Proportion of time spent on this operation. */
|
||||
|
||||
OpDetailInfo() = default;
|
||||
OpDetailInfo(const std::shared_ptr<OpInfo> op_info, float proportion);
|
||||
|
||||
std::string GetCpuHeader() const {
|
||||
return "op_side,op_type,op_name,full_op_name,op_occurrences,op_total_time(ms),"
|
||||
"op_avg_time(ms),total_proportion,subgraph,pid";
|
||||
}
|
||||
std::string GetGpuHeader() const {
|
||||
return "op_side,op_type,op_name,op_full_name,op_occurrences,op_total_time(us),op_avg_time(us),total_proportion,"
|
||||
"cuda_activity_cost_time(us),cuda_activity_call_count";
|
||||
}
|
||||
std::string GetCpuHeader() const;
|
||||
std::string GetGpuHeader() const;
|
||||
|
||||
void OutputCpuOpDetailInfo(std::ostream &os) const {
|
||||
os << "Host," << op_type_ << ',' << op_name_ << ',' << op_full_name_ << ',' << op_info_->op_count << ','
|
||||
<< op_info_->op_host_cost_time << ',' << op_avg_time_ << ',' << proportion_ << ",Default," << op_info_->pid
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void OutputGpuOpDetailInfo(std::ostream &os) const {
|
||||
os << "Device," << op_type_ << ',' << op_name_ << ',' << op_full_name_ << ',' << op_info_->op_count << ','
|
||||
<< op_info_->op_host_cost_time << ',' << op_avg_time_ << ',' << proportion_ << ','
|
||||
<< op_info_->cupti_activity_time << ',' << op_info_->op_kernel_count << std::endl;
|
||||
}
|
||||
void OutputCpuOpDetailInfo(std::ostream &os) const;
|
||||
void OutputGpuOpDetailInfo(std::ostream &os) const;
|
||||
};
|
||||
|
||||
// Structure to store information about an operation type
|
||||
struct OpType {
|
||||
std::string op_type_;
|
||||
int count_{0};
|
||||
int step_{0};
|
||||
float total_time_{0};
|
||||
float avg_time_{0};
|
||||
float proportion_{0};
|
||||
std::string op_type_; /**< Type of the operation. */
|
||||
int count_{0}; /**< Count of this operation type. */
|
||||
int step_{0}; /**< Step count. */
|
||||
float total_time_{0}; /**< Total time spent on this operation type. */
|
||||
float avg_time_{0}; /**< Average time for this operation type. */
|
||||
float proportion_{0}; /**< Proportion of time spent on this operation type. */
|
||||
|
||||
std::string GetCpuHeader() const {
|
||||
return "op_type,type_occurrences,execution_frequency(per-step),"
|
||||
"total_compute_time,avg_time(ms),percent";
|
||||
}
|
||||
std::string GetGpuHeader() const { return "op_type,type_occurrences,total_time(us),total_proportion,avg_time(us)"; }
|
||||
std::string GetCpuHeader() const;
|
||||
std::string GetGpuHeader() const;
|
||||
|
||||
void OutputCpuOpTypeInfo(std::ostream &os) const {
|
||||
if (step_ == 0) {
|
||||
MS_LOG(ERROR) << "The run step can not be 0.";
|
||||
return;
|
||||
}
|
||||
if (count_ == 0) {
|
||||
MS_LOG(ERROR) << "The num of operation type can not be 0.";
|
||||
return;
|
||||
}
|
||||
os << op_type_ << ',' << count_ << ',' << count_ / step_ << ',' << total_time_ << ',' << total_time_ / count_ << ','
|
||||
<< proportion_ << std::endl;
|
||||
}
|
||||
void OutputCpuOpTypeInfo(std::ostream &os) const;
|
||||
void OutputGpuOpTypeInfo(std::ostream &os) const;
|
||||
|
||||
void OutputGpuOpTypeInfo(std::ostream &os) const {
|
||||
os << op_type_ << ',' << count_ << ',' << total_time_ << ',' << proportion_ << ',' << avg_time_ << std::endl;
|
||||
}
|
||||
|
||||
OpType &operator+=(const OpType &other) {
|
||||
this->count_ += other.count_;
|
||||
this->total_time_ += other.total_time_;
|
||||
this->proportion_ += other.proportion_;
|
||||
return *this;
|
||||
}
|
||||
OpType &operator+=(const OpType &other);
|
||||
};
|
||||
|
||||
using OpTimestampInfo = std::unordered_map<std::string, std::vector<StartDuration>>; // <op_full_name, StartDuration>
|
||||
// Aliases for unordered maps and vectors
|
||||
using OpTimestampInfo = std::unordered_map<std::string, std::vector<StartDuration>>;
|
||||
using OpInfoMap = std::unordered_map<std::string, OpInfo>;
|
||||
using OpTypeInfos = std::unordered_map<std::string, OpType>; // <op_full_name, Optype>
|
||||
using OpTypeInfos = std::unordered_map<std::string, OpType>;
|
||||
using OpDetailInfos = std::vector<OpDetailInfo>;
|
||||
|
||||
class DataSaver {
|
||||
public:
|
||||
DataSaver() = default;
|
||||
|
||||
virtual ~DataSaver() = default;
|
||||
|
||||
void ParseOpInfo(const OpInfoMap &op_info_maps);
|
||||
|
|
@ -114,16 +79,47 @@ class DataSaver {
|
|||
OpTimestampInfo op_timestamps_map_;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Adds detailed information about an operation type.
|
||||
*
|
||||
* @param[in] op_detail_info Information about the operation type.
|
||||
*/
|
||||
void AddOpDetailInfoForType(const OpDetailInfo &op_detail_info);
|
||||
|
||||
/**
|
||||
* @brief Calculates the total time spent on all operations.
|
||||
*
|
||||
* @param[in] op_info_maps A map containing information about operations.
|
||||
* @return Total time spent on all operations.
|
||||
*/
|
||||
float GetTotalOpTime(const OpInfoMap &op_info_maps) const;
|
||||
|
||||
/**
|
||||
* @brief Writes information about operation types to a file.
|
||||
*
|
||||
* @param[in] saver_base_dir Base directory for saving data.
|
||||
*/
|
||||
void WriteOpType(const std::string &saver_base_dir) const;
|
||||
|
||||
/**
|
||||
* @brief Writes detailed information about operations to a file.
|
||||
*
|
||||
* @param[in] saver_base_dir Base directory for saving data.
|
||||
*/
|
||||
void WriteOpDetail(const std::string &saver_base_dir) const;
|
||||
|
||||
/**
|
||||
* @brief Writes timestamps for operations to a file.
|
||||
*
|
||||
* @param[in] saver_base_dir Base directory for saving data.
|
||||
*/
|
||||
void WriteOpTimestamp(const std::string &saver_base_dir) const;
|
||||
|
||||
/**
|
||||
* @brief Changes the file mode of the specified file.
|
||||
*
|
||||
* @param[in] file_path Path to the file.
|
||||
*/
|
||||
void ChangeFileMode(const std::string &file_path) const;
|
||||
|
||||
OpTypeInfos op_type_infos_;
|
||||
|
|
@ -131,6 +127,7 @@ class DataSaver {
|
|||
std::string op_side_;
|
||||
std::string device_id_;
|
||||
};
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace mindspore
|
||||
|
||||
|
|
|
|||
|
|
@ -21,53 +21,66 @@
|
|||
namespace mindspore {
|
||||
namespace profiler {
|
||||
namespace gpu {
|
||||
// LoadLibrary function
|
||||
// This function loads a shared library using dlopen and returns the handle.
|
||||
// It throws an exception if loading fails.
|
||||
inline void *LoadLibrary(const char *name) {
|
||||
// Load the shared library with RTLD_LAZY and RTLD_LOCAL flags
|
||||
auto handle = dlopen(name, RTLD_LAZY | RTLD_LOCAL);
|
||||
if (handle == nullptr) {
|
||||
// Throw an exception with an error message if loading fails
|
||||
MS_LOG(EXCEPTION) << "Load lib " << name << " Please check whether configured the path of CUPTI to LD_LIBRARY_PATH";
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
// GetCUPTIHandle function
|
||||
// This function gets the handle to the CUPTI library using LoadLibrary.
|
||||
// It ensures that the handle is obtained only once and returns it.
|
||||
inline void *GetCUPTIHandle() {
|
||||
// Static variable to store the CUPTI handle
|
||||
static void *handle = LoadLibrary("libcupti.so");
|
||||
return handle;
|
||||
}
|
||||
|
||||
// GetCUPTIFunc function
|
||||
// This function gets a function pointer from the CUPTI library handle.
|
||||
// It ensures that the function pointer is obtained only once and returns it.
|
||||
inline void *GetCUPTIFunc(const char *name) {
|
||||
// Get the CUPTI library handle using GetCUPTIHandle
|
||||
static void *handle = GetCUPTIHandle();
|
||||
|
||||
// Get the function pointer by name from the CUPTI library
|
||||
void *func = dlsym(handle, name);
|
||||
if (func == nullptr) {
|
||||
// Throw an exception with an error message if obtaining the function pointer fails
|
||||
MS_LOG(EXCEPTION) << "Load func " << name << " failed, make sure you have implied it!";
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
// Define function pointer types for various CUPTI functions
|
||||
using CuptiSubscribeFunc = CUptiResult (*)(CUpti_SubscriberHandle *subscriber, CUpti_CallbackFunc callback,
|
||||
void *userdata);
|
||||
using CuptiEnableDomainFunc = CUptiResult (*)(uint32_t enable, CUpti_SubscriberHandle subscriber,
|
||||
CUpti_CallbackDomain domain);
|
||||
using CuptiActivityEnableFunc = CUptiResult (*)(CUpti_ActivityKind kind);
|
||||
using CuptiActivityRegisterCallbacksFunc = CUptiResult (*)(CUpti_BuffersCallbackRequestFunc funcBufferRequested,
|
||||
CUpti_BuffersCallbackCompleteFunc funcBufferCompleted);
|
||||
using CuptiUnsubscribeFunc = CUptiResult (*)(CUpti_SubscriberHandle subscriber);
|
||||
using CuptiActivityFlushAllFunc = CUptiResult (*)(uint32_t flag);
|
||||
using CuptiActivityDisableFunc = CUptiResult (*)(CUpti_ActivityKind kind);
|
||||
using CuptiActivityGetNextRecordFunc = CUptiResult (*)(uint8_t *buffer, size_t validBufferSizeBytes,
|
||||
CUpti_Activity **record);
|
||||
using CuptiActivityGetNumDroppedRecordsFunc = CUptiResult (*)(CUcontext context, uint32_t streamId, size_t *dropped);
|
||||
using CuptiGetTimestampFunc = CUptiResult (*)(uint64_t *timestamp);
|
||||
using CuptiGetResultStringFunc = CUptiResult (*)(CUptiResult result, const char **str);
|
||||
using CuptiGetStreamIdFunc = CUptiResult (*)(CUcontext context, CUstream stream, uint32_t *streamId);
|
||||
using CuptiGetDeviceIdFunc = CUptiResult (*)(CUcontext context, uint32_t *deviceId);
|
||||
// ... Define function pointer types for other CUPTI functions ...
|
||||
|
||||
// CuptiSubscribe function
|
||||
// This function calls the CuptiSubscribeFunc obtained from GetCUPTIFunc.
|
||||
CUptiResult CuptiSubscribe(CUpti_SubscriberHandle *subscriber, CUpti_CallbackFunc callback, void *userdata) {
|
||||
// Get the function pointer using GetCUPTIFunc
|
||||
static auto func_ptr = reinterpret_cast<CuptiSubscribeFunc>(GetCUPTIFunc("cuptiSubscribe"));
|
||||
// Call the function pointer and return the result
|
||||
return func_ptr(subscriber, callback, userdata);
|
||||
}
|
||||
|
||||
// CuptiEnableDomain function
|
||||
// This function calls the CuptiEnableDomainFunc obtained from GetCUPTIFunc.
|
||||
CUptiResult CuptiEnableDomain(uint32_t enable, CUpti_SubscriberHandle subscriber, CUpti_CallbackDomain domain) {
|
||||
// Get the function pointer using GetCUPTIFunc
|
||||
static auto func_ptr = reinterpret_cast<CuptiEnableDomainFunc>(GetCUPTIFunc("cuptiEnableDomain"));
|
||||
// Call the function pointer and return the result
|
||||
return func_ptr(enable, subscriber, domain);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,11 @@
|
|||
namespace mindspore {
|
||||
namespace profiler {
|
||||
namespace gpu {
|
||||
const size_t BUF_SIZE = 32 * 1024;
|
||||
const size_t ALIGN_SIZE = 8;
|
||||
// Constants for buffer size and alignment
|
||||
constexpr size_t BUF_SIZE = 32 * 1024;
|
||||
constexpr size_t ALIGN_SIZE = 8;
|
||||
|
||||
// Helper macro for checking CUPTI return values with error handling
|
||||
#define CHECK_CUPTI_RET_WITH_ERROR(expression, message) \
|
||||
if ((expression) != CUPTI_SUCCESS) { \
|
||||
const char *errstr; \
|
||||
|
|
@ -44,12 +47,15 @@ const size_t ALIGN_SIZE = 8;
|
|||
<< " GPU performance tuning document on the official website of mindinsight."; \
|
||||
}
|
||||
|
||||
// Helper macro for checking CUPTI return values with exception handling
|
||||
#define CHECK_CUPTI_RET_WITH_EXCEPT(expression, message) \
|
||||
if ((expression) != CUPTI_SUCCESS) { \
|
||||
const char *errstr; \
|
||||
CuptiGetResultString(expression, &errstr); \
|
||||
MS_LOG(EXCEPTION) << "CUPTI Error:" << errstr << " function:" << (message); \
|
||||
}
|
||||
|
||||
// Helper macro for checking CUDA return values with error handling
|
||||
#define CHECK_CUDA_RET_WITH_ERROR(expression, message) \
|
||||
do { \
|
||||
cudaError_t status = (expression); \
|
||||
|
|
@ -58,6 +64,8 @@ const size_t ALIGN_SIZE = 8;
|
|||
<< cudaGetErrorString(status); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Helper macro for checking the validity of a pointer and logging an error if it's nullptr
|
||||
#define PROFILER_ERROR_IF_NULLPTR(ptr) \
|
||||
do { \
|
||||
if ((ptr) == nullptr) { \
|
||||
|
|
@ -66,31 +74,40 @@ const size_t ALIGN_SIZE = 8;
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
// Static member initialization
|
||||
std::shared_ptr<GPUProfiler> GPUProfiler::profiler_inst_ = std::make_shared<GPUProfiler>();
|
||||
|
||||
// GetThreadID function
|
||||
// This function returns the ID of the current thread using pthread_self.
|
||||
int32_t GetThreadID() {
|
||||
uint32_t thread_id = static_cast<uint32_t>(pthread_self());
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
// GetStreamID function
|
||||
// This function returns the ID of the CUDA stream associated with a given context and stream pointer.
|
||||
uint32_t GetStreamID(const CUcontext context, const void *stream) {
|
||||
uint32_t stream_id = 0;
|
||||
if (stream != nullptr) {
|
||||
CHECK_CUPTI_RET_WITH_ERROR(CuptiGetStreamId(context, (CUstream)stream, &stream_id), "CuptiGetStreamId");
|
||||
if (CuptiGetStreamId(context, (CUstream)stream, &stream_id) != CUPTI_SUCCESS) {
|
||||
MS_LOG(ERROR) << "Training process unexpectedly stopped, profiling data cannot be write to file"
|
||||
MS_LOG(ERROR) << "Training process unexpectedly stopped, profiling data cannot be written to a file."
|
||||
<< "To obtain the profiling data, do not interrupt the training process.";
|
||||
}
|
||||
}
|
||||
return stream_id;
|
||||
}
|
||||
|
||||
// GetCUPTITimeStamp function
|
||||
// This function returns the current CUPTI timestamp.
|
||||
uint64_t GetCUPTITimeStamp() {
|
||||
uint64_t time_stamp = 0l;
|
||||
CHECK_CUPTI_RET_WITH_ERROR(CuptiGetTimestamp(&time_stamp), "CuptiGetTimestamp");
|
||||
return time_stamp;
|
||||
}
|
||||
|
||||
// GetHostTimeStamp function
|
||||
// This function returns the current host timestamp in nanoseconds.
|
||||
uint64_t GetHostTimeStamp() {
|
||||
auto cur_sys_clock = std::chrono::system_clock::now();
|
||||
uint64_t cur_time_stamp =
|
||||
|
|
@ -98,6 +115,8 @@ uint64_t GetHostTimeStamp() {
|
|||
return cur_time_stamp;
|
||||
}
|
||||
|
||||
// GetKernelFunc function
|
||||
// This function demangles the kernel function name if possible and returns it.
|
||||
std::string GetKernelFunc(const char *name) {
|
||||
char *demangledName = abi::__cxa_demangle(name, nullptr, nullptr, nullptr);
|
||||
if (demangledName != nullptr) {
|
||||
|
|
@ -107,6 +126,8 @@ std::string GetKernelFunc(const char *name) {
|
|||
}
|
||||
}
|
||||
|
||||
// IsMemcpyAsyncEvent function
|
||||
// This function checks if a CUPTI callback ID represents an asynchronous memory copy event.
|
||||
bool IsMemcpyAsyncEvent(CUpti_CallbackId cb_id) {
|
||||
switch (cb_id) {
|
||||
case CUPTI_DRIVER_TRACE_CBID_cuMemcpyAsync:
|
||||
|
|
@ -125,6 +146,8 @@ bool IsMemcpyAsyncEvent(CUpti_CallbackId cb_id) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// IsMemcpySyncEvent function
|
||||
// This function checks if a CUPTI callback ID represents a synchronous memory copy event.
|
||||
bool IsMemcpySyncEvent(CUpti_CallbackId cb_id) {
|
||||
switch (cb_id) {
|
||||
case CUPTI_DRIVER_TRACE_CBID_cuMemcpy:
|
||||
|
|
@ -144,9 +167,10 @@ bool IsMemcpySyncEvent(CUpti_CallbackId cb_id) {
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// CUPTIApiExit function
|
||||
// This function handles CUPTI callback exit events and processes the profiling data.
|
||||
void CUPTIApiExit(const std::shared_ptr<GPUProfiler> &gpu_profiler_inst, CUpti_CallbackId cb_id,
|
||||
const CUpti_CallbackData *cb_data) {
|
||||
uint64_t start_timestamp = *cb_data->correlationData;
|
||||
|
|
@ -179,6 +203,8 @@ void CUPTIApiExit(const std::shared_ptr<GPUProfiler> &gpu_profiler_inst, CUpti_C
|
|||
}
|
||||
}
|
||||
|
||||
// CUPTICallBackFunc function
|
||||
// This function is the callback function for CUPTI API events and handles entry and exit events.
|
||||
void CUPTICallBackFunc(void *user_data, CUpti_CallbackDomain domain, CUpti_CallbackId cb_id,
|
||||
const CUpti_CallbackData *cb_data) {
|
||||
if (domain != CUPTI_CB_DOMAIN_DRIVER_API) {
|
||||
|
|
@ -204,8 +230,10 @@ void CUPTICallBackFunc(void *user_data, CUpti_CallbackDomain domain, CUpti_Callb
|
|||
}
|
||||
}
|
||||
|
||||
// GetKernelFuncName function
|
||||
// This function extracts the kernel function name from a full kernel name.
|
||||
std::string GetKernelFuncName(std::string kernel_name) {
|
||||
// remove the return type name (void) in kernel_name.
|
||||
// Remove the return type name (void) in kernel_name.
|
||||
std::string search_pattern("void ");
|
||||
auto func_name_begin_iter = kernel_name.find(search_pattern);
|
||||
if (func_name_begin_iter == kernel_name.npos) {
|
||||
|
|
@ -216,36 +244,56 @@ std::string GetKernelFuncName(std::string kernel_name) {
|
|||
return kernel_name.substr(func_name_begin_iter);
|
||||
}
|
||||
|
||||
// GetInstance function
|
||||
// This function returns the singleton instance of the GPUProfiler class.
|
||||
std::shared_ptr<GPUProfiler> &GPUProfiler::GetInstance() {
|
||||
MS_EXCEPTION_IF_NULL(profiler_inst_);
|
||||
return profiler_inst_;
|
||||
}
|
||||
|
||||
// GPUProfiler::SyncEnable function
|
||||
// This function sets the synchronous profiling enable flag for the GPU profiler.
|
||||
void GPUProfiler::SyncEnable(const bool enable_flag) {
|
||||
// Log the synchronous profiling enable flag
|
||||
MS_LOG(INFO) << "GPU Profiler synchronous enable flag:" << enable_flag;
|
||||
// Set the synchronous enable flag
|
||||
sync_enable_flag_ = enable_flag;
|
||||
}
|
||||
|
||||
// GPUProfiler::StepProfilingEnable function
|
||||
// This function sets the step profiling enable flag for the GPU profiler.
|
||||
void GPUProfiler::StepProfilingEnable(const bool enable_flag) {
|
||||
// Log the step profiling enable flag
|
||||
MS_LOG(INFO) << "GPU Profiler enable flag:" << enable_flag;
|
||||
// Flush all CUPTI activities
|
||||
CHECK_CUPTI_RET_WITH_ERROR(CuptiActivityFlushAll(0), "CuptiActivityFlushAll");
|
||||
// Set the step profiling enable flag
|
||||
enable_flag_ = enable_flag;
|
||||
}
|
||||
|
||||
// GPUProfiler::FixOpNameByCorrelationId function
|
||||
// This function fixes the operation name based on the correlation ID in the event.
|
||||
void GPUProfiler::FixOpNameByCorrelationId(Event *event) {
|
||||
// Check if the event is an activity event
|
||||
PROFILER_ERROR_IF_NULLPTR(event);
|
||||
if (event->api_type != CUPTIApiType::kActivity) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the operation name in the op_name_map_ using the correlation ID
|
||||
auto iter = op_name_map_.find(event->correlation_id);
|
||||
if (iter != op_name_map_.end()) {
|
||||
event->op_name = std::move(iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
// GPUProfiler::AddEvent function
|
||||
// This function adds an event to the GPU profiler's event list, handling concurrency for different types of events.
|
||||
void GPUProfiler::AddEvent(Event &&event) {
|
||||
// protect callback concurrency for driver api and activity
|
||||
// Protect callback concurrency for driver API and activity
|
||||
std::unique_lock<std::mutex> lock(event_mutex_);
|
||||
|
||||
// Determine the type of the event and add it to the appropriate event list
|
||||
switch (event.api_type) {
|
||||
case CUPTIApiType::kCallback: {
|
||||
if (cupti_callback_events_count_ < max_cupti_callback_events_) {
|
||||
|
|
@ -270,6 +318,8 @@ void GPUProfiler::AddEvent(Event &&event) {
|
|||
}
|
||||
}
|
||||
|
||||
// GPUProfiler::EventLog function
|
||||
// This function logs information about an event, including its details and timestamps.
|
||||
void GPUProfiler::EventLog(const Event &event) {
|
||||
MS_LOG(DEBUG) << "GPUProfiler"
|
||||
<< ",\"kernel_name:" << event.kernel_name << "\",kernel_type:" << event.kernel_type
|
||||
|
|
@ -281,30 +331,36 @@ void GPUProfiler::EventLog(const Event &event) {
|
|||
<< ",stream_id:" << event.stream_id << ",cb_id:" << event.cb_id;
|
||||
}
|
||||
|
||||
// GPUProfiler::ProcessEvents function
|
||||
// This function processes the collected events, updating operation-specific profiling information.
|
||||
void GPUProfiler::ProcessEvents() {
|
||||
for (Event &event : events_) {
|
||||
// Fix operation names based on correlation IDs
|
||||
if (event.op_name.empty()) {
|
||||
FixOpNameByCorrelationId(&event);
|
||||
}
|
||||
|
||||
// Log the event details
|
||||
EventLog(event);
|
||||
|
||||
// Skip events with empty operation names or stream synchronization events
|
||||
if (event.op_name.empty() || event.cb_id == CUPTI_DRIVER_TRACE_CBID_cuStreamSynchronize) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update profiling information for the associated operation
|
||||
auto iter = op_info_map_.find(event.op_name);
|
||||
if (iter != op_info_map_.end()) {
|
||||
switch (event.api_type) {
|
||||
case CUPTIApiType::kCallback: {
|
||||
iter->second.op_kernel_api_count += 1;
|
||||
// The time unit from ns to us
|
||||
// Convert and accumulate the time unit from ns to us
|
||||
iter->second.cupti_api_call_time += (event.end_time_stamp - event.start_time_stamp) / kTimeUnit;
|
||||
break;
|
||||
}
|
||||
case CUPTIApiType::kActivity: {
|
||||
iter->second.op_kernel_count += 1;
|
||||
// The time unit from ns to us
|
||||
// Convert and accumulate the time unit from ns to us
|
||||
iter->second.cupti_activity_time += (event.end_time_stamp - event.start_time_stamp) / kTimeUnit;
|
||||
break;
|
||||
}
|
||||
|
|
@ -315,10 +371,14 @@ void GPUProfiler::ProcessEvents() {
|
|||
}
|
||||
}
|
||||
|
||||
// GPUProfiler::OpsParser function
|
||||
// This function parses and analyzes the collected events to generate profiling information.
|
||||
void GPUProfiler::OpsParser() {
|
||||
// Log information about the collected events
|
||||
MS_LOG(INFO) << "Count the number of events size:" << events_.size()
|
||||
<< " callback api:" << cupti_callback_events_count_ << " activity:" << cupti_activity_events_count_;
|
||||
|
||||
// Check for dropped events and log a warning if any
|
||||
if (cupti_activity_events_drop_count_ > 0 || cupti_callback_events_drop_count_ > 0) {
|
||||
MS_LOG(WARNING)
|
||||
<< "The total number of events exceeded the profiler's processing capacity, some events were discarded."
|
||||
|
|
@ -326,23 +386,32 @@ void GPUProfiler::OpsParser() {
|
|||
<< " callback api events:" << cupti_callback_events_drop_count_;
|
||||
}
|
||||
|
||||
// If no events were collected, return
|
||||
if (events_.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process and analyze the collected events
|
||||
ProcessEvents();
|
||||
|
||||
// Log the generated profiling information
|
||||
MS_LOG(DEBUG) << "GPU_profiler, op_name, op_count , kernel_count, kernel_api_count,|"
|
||||
",cupti_activity_total_time, cupti_api_call_total_time, op_host_cost_total_time,|"
|
||||
",cupti_activity_average_time,cupti_api_call_average_time, op_host_cost_average_time"
|
||||
<< std::endl;
|
||||
|
||||
// Create a vector of ordered operation information based on cupti_activity_time
|
||||
std::vector<std::pair<std::string, OpInfo>> order_vec(op_info_map_.begin(), op_info_map_.end());
|
||||
|
||||
// Define a comparison function for sorting
|
||||
auto cmp_func = [](const std::pair<std::string, OpInfo> &a, const std::pair<std::string, OpInfo> &b) {
|
||||
return a.second.cupti_activity_time > b.second.cupti_activity_time;
|
||||
};
|
||||
|
||||
// Sort the operation information vector
|
||||
std::sort(order_vec.begin(), order_vec.end(), cmp_func);
|
||||
|
||||
// Log the profiling information for each operation
|
||||
for (auto iter = order_vec.begin(); iter != order_vec.end(); iter++) {
|
||||
if (iter->second.op_count == 0) {
|
||||
MS_LOG(ERROR) << "The num of operations can not be 0.";
|
||||
|
|
@ -359,6 +428,8 @@ void GPUProfiler::OpsParser() {
|
|||
}
|
||||
}
|
||||
|
||||
// GPUProfiler::EventHandleProcess function
|
||||
// This function processes a CUPTI callback event and creates an Event object to add to the profiler's event list.
|
||||
void GPUProfiler::EventHandleProcess(CUpti_CallbackId cbid, const CUpti_CallbackData *cbdata,
|
||||
const std::string &typestring, uint64_t startTimestamp, uint64_t endTimestamp) {
|
||||
Event event;
|
||||
|
|
@ -380,22 +451,34 @@ void GPUProfiler::EventHandleProcess(CUpti_CallbackId cbid, const CUpti_Callback
|
|||
op_name_map_[event.correlation_id] = event.op_name;
|
||||
AddEvent(std::move(event));
|
||||
}
|
||||
|
||||
// ActivityAllocBuffer function
|
||||
// This function allocates memory buffer for CUPTI activity records.
|
||||
void CUPTIAPI ActivityAllocBuffer(uint8_t **buffer, size_t *size, size_t *maxNumRecords);
|
||||
|
||||
// ActivityProcessBuffer function
|
||||
// This function processes the CUPTI activity records in the buffer.
|
||||
void CUPTIAPI ActivityProcessBuffer(CUcontext ctx, uint32_t streamId, uint8_t *buffer, size_t size, size_t validSize);
|
||||
|
||||
// Init function
|
||||
// This function initializes the GPU Profiler, subscribing to CUPTI callbacks and enabling specific activity kinds.
|
||||
void GPUProfiler::Init(const std::string &profileDataPath = "") {
|
||||
MS_LOG(INFO) << "Initialize GPU Profiling";
|
||||
|
||||
// Check if the GPU Profiler has already been initialized
|
||||
if (subscriber_ != nullptr) {
|
||||
// If already initialized, stop CUPTI and raise an exception
|
||||
StopCUPTI();
|
||||
MS_LOG(EXCEPTION)
|
||||
<< "Repeated initialization, Please check whether you have created the Profiler object multiple times";
|
||||
MS_LOG(EXCEPTION) << "Repeated initialization, Please check whether you have created the Profiler object multiple times";
|
||||
}
|
||||
|
||||
// Subscribe to CUPTI callbacks using CUPTICallBackFunc
|
||||
CHECK_CUPTI_RET_WITH_EXCEPT(CuptiSubscribe(&subscriber_, (CUpti_CallbackFunc)CUPTICallBackFunc, this),
|
||||
"CuptiSubscribe");
|
||||
|
||||
// Enable CUPTI domain for driver API callbacks
|
||||
CHECK_CUPTI_RET_WITH_EXCEPT(CuptiEnableDomain(1, subscriber_, CUPTI_CB_DOMAIN_DRIVER_API), "CuptiEnableDomain");
|
||||
|
||||
// Enable specific CUPTI activity kinds (Memory copies and Kernel activities)
|
||||
activities_enable_.emplace_back(CUPTI_ACTIVITY_KIND_MEMCPY);
|
||||
activities_enable_.emplace_back(CUPTI_ACTIVITY_KIND_MEMCPY2);
|
||||
activities_enable_.emplace_back(CUPTI_ACTIVITY_KIND_KERNEL);
|
||||
|
|
@ -405,36 +488,48 @@ void GPUProfiler::Init(const std::string &profileDataPath = "") {
|
|||
CHECK_CUPTI_RET_WITH_EXCEPT(CuptiActivityEnable(*it), "CuptiActivityEnable");
|
||||
}
|
||||
|
||||
// Register callback functions for allocating and processing CUPTI activity records
|
||||
CHECK_CUPTI_RET_WITH_EXCEPT(CuptiActivityRegisterCallbacks(ActivityAllocBuffer, ActivityProcessBuffer),
|
||||
"CuptiActivityRegisterCallbacks");
|
||||
|
||||
// Get the start timestamps for GPU and host
|
||||
base_time_.gpu_start_time = GetCUPTITimeStamp();
|
||||
base_time_.host_start_time = GetHostTimeStamp();
|
||||
base_time_.host_start_monotonic_raw_time = GetHostMonoTimeStamp();
|
||||
|
||||
// Set the profile data path and log relevant information
|
||||
profile_data_path_ = profileDataPath;
|
||||
MS_LOG(INFO) << "GPU start time(ns):" << base_time_.gpu_start_time
|
||||
<< " Host start time(ns):" << base_time_.host_start_time << " profile data path: " << profile_data_path_;
|
||||
is_init_ = true;
|
||||
}
|
||||
|
||||
// SetRunTimeData function
|
||||
// This function sets runtime data for a specific operation.
|
||||
void GPUProfiler::SetRunTimeData(const std::string &op_name, void *stream) {
|
||||
// Check if the operation already exists in the op_info_map
|
||||
auto iter = op_info_map_.find(op_name);
|
||||
if (iter != op_info_map_.end()) {
|
||||
// If it exists, increment the operation count
|
||||
iter->second.op_count += 1;
|
||||
} else {
|
||||
// If it doesn't exist, create a new OpInfo entry and set its data
|
||||
OpInfo op_info;
|
||||
op_info.op_name = op_name;
|
||||
op_info.stream = stream;
|
||||
op_info.op_count = 1;
|
||||
op_info_map_[op_name] = op_info;
|
||||
}
|
||||
// Set the current operation name and stream
|
||||
op_name_ = op_name;
|
||||
stream_ = stream;
|
||||
}
|
||||
|
||||
// OpDataProducerBegin function
|
||||
// This function marks the beginning of an operation's data production and records start timestamps.
|
||||
void GPUProfiler::OpDataProducerBegin(const std::string op_name, void *stream) {
|
||||
if (sync_enable_flag_) {
|
||||
// Create CUDA events for synchronization if sync_enable_flag_ is true
|
||||
CHECK_CUDA_RET_WITH_ERROR(cudaEventCreate(&op_event_start_), "cudaEventCreate op event start failed");
|
||||
CHECK_CUDA_RET_WITH_ERROR(cudaEventCreate(&op_event_stop_), "cudaEventCreate op event stop failed");
|
||||
CHECK_CUDA_RET_WITH_ERROR(cudaEventRecord(op_event_start_, (CUstream)stream_),
|
||||
|
|
@ -442,66 +537,103 @@ void GPUProfiler::OpDataProducerBegin(const std::string op_name, void *stream) {
|
|||
op_host_time_start_ = GetHostTimeStamp();
|
||||
op_cupti_time_start_ = GetCUPTITimeStamp();
|
||||
} else {
|
||||
// Record timestamps without synchronization
|
||||
op_host_time_start_ = GetHostTimeStamp();
|
||||
op_cupti_time_start_ = GetCUPTITimeStamp();
|
||||
}
|
||||
// Set runtime data for the current operation
|
||||
SetRunTimeData(op_name, stream);
|
||||
|
||||
// Record the start time for the operation if MindRT is enabled
|
||||
if (MsContext::GetInstance()->get_param<bool>(MS_CTX_ENABLE_MINDRT)) {
|
||||
RecordOneStepStartEndInfo(op_name);
|
||||
}
|
||||
}
|
||||
|
||||
// SingleOpLaunchTimeProcess function
|
||||
// This function processes the launch time of a single operation.
|
||||
void GPUProfiler::SingleOpLaunchTimeProcess(float op_time_elapsed) {
|
||||
// Get the launch end time
|
||||
auto launch_end_time = GetTime();
|
||||
// Calculate the launch start time
|
||||
double launch_start_time = launch_end_time - op_time_elapsed / kTimeUnit / kTimeUnit;
|
||||
// Set the single operation launch time as a pair of start and end times
|
||||
SetSingleOpLaunchTime(std::make_pair(launch_start_time, launch_end_time));
|
||||
}
|
||||
|
||||
// OpDataProducerEnd function
|
||||
// This function marks the end of an operation's data production and calculates the elapsed time.
|
||||
void GPUProfiler::OpDataProducerEnd() {
|
||||
float op_time_elapsed = 0;
|
||||
if (sync_enable_flag_) {
|
||||
// Record end event and synchronize if sync_enable_flag_ is true
|
||||
CHECK_CUDA_RET_WITH_ERROR(cudaEventRecord(op_event_stop_, (CUstream)stream_),
|
||||
"cudaEventRecord op event stop failed");
|
||||
CHECK_CUDA_RET_WITH_ERROR(cudaEventSynchronize(op_event_start_), "cudaEventSynchronize op event start failed");
|
||||
CHECK_CUDA_RET_WITH_ERROR(cudaEventSynchronize(op_event_stop_), "cudaEventSynchronize op event stop failed");
|
||||
// Calculate the elapsed time using CUDA events
|
||||
CHECK_CUDA_RET_WITH_ERROR(cudaEventElapsedTime(&op_time_elapsed, op_event_start_, op_event_stop_),
|
||||
"cudaEventElapsedTime failed");
|
||||
// Destroy CUDA events
|
||||
CHECK_CUDA_RET_WITH_ERROR(cudaEventDestroy(op_event_start_), "cudaEventDestroy op event start failed");
|
||||
CHECK_CUDA_RET_WITH_ERROR(cudaEventDestroy(op_event_stop_), "cudaEventDestroy op event stop failed");
|
||||
// Convert elapsed time to microseconds
|
||||
op_time_elapsed = op_time_elapsed * kTimeUnit;
|
||||
// Record the host stop time
|
||||
op_host_time_stop_ = GetHostTimeStamp();
|
||||
// Process the launch time for the single operation
|
||||
SingleOpLaunchTimeProcess(op_time_elapsed);
|
||||
} else {
|
||||
// Calculate elapsed time without synchronization
|
||||
op_host_time_stop_ = GetHostTimeStamp();
|
||||
op_time_elapsed = (op_host_time_stop_ - op_host_time_start_) / kTimeUnit;
|
||||
// Process the launch time for the single operation
|
||||
SingleOpLaunchTimeProcess(op_time_elapsed);
|
||||
}
|
||||
// Log the host elapsed time for the operation
|
||||
MS_LOG(DEBUG) << "Host Time Elapsed(us)," << op_name_ << "," << op_time_elapsed;
|
||||
// Set runtime data for the operation with elapsed time
|
||||
Profiler::SetRunTimeData(op_name_, op_time_elapsed);
|
||||
Profiler::SetRunTimeData(op_name_, op_cupti_time_start_, op_time_elapsed);
|
||||
}
|
||||
|
||||
// StopCUPTI function
|
||||
// This function stops CUPTI profiling, unsubscribing and disabling CUPTI activities.
|
||||
void GPUProfiler::StopCUPTI() {
|
||||
if (subscriber_ != nullptr) {
|
||||
// Unsubscribe from CUPTI and flush activity records
|
||||
CHECK_CUPTI_RET_WITH_ERROR(CuptiUnsubscribe(subscriber_), "CuptiUnsubscribe");
|
||||
CHECK_CUPTI_RET_WITH_ERROR(CuptiActivityFlushAll(0), "CuptiActivityFlushAll");
|
||||
// Disable CUPTI activities
|
||||
for (std::vector<CUpti_ActivityKind>::iterator it = activities_enable_.begin(); it != activities_enable_.end();
|
||||
++it) {
|
||||
CHECK_CUPTI_RET_WITH_ERROR(CuptiActivityDisable(*it), "CuptiActivityDisable");
|
||||
}
|
||||
// Reset the subscriber to nullptr
|
||||
subscriber_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop function
|
||||
// This function stops GPU profiling and performs necessary cleanup.
|
||||
void GPUProfiler::Stop() {
|
||||
MS_LOG(INFO) << "Stop GPU Profiling";
|
||||
|
||||
// Stop CUPTI profiling
|
||||
StopCUPTI();
|
||||
|
||||
// Parse and process profiling operations
|
||||
OpsParser();
|
||||
|
||||
// Save profile data to the specified path
|
||||
SaveProfileData();
|
||||
|
||||
// Clear the profiling instance
|
||||
ClearInst();
|
||||
}
|
||||
|
||||
// SaveExtraProfileData function
|
||||
// This function saves extra profiling data for each profiling operation.
|
||||
void GPUProfiler::SaveExtraProfileData() {
|
||||
for (auto op : profiling_op_) {
|
||||
op.second->SaveProfilingData();
|
||||
|
|
@ -509,18 +641,29 @@ void GPUProfiler::SaveExtraProfileData() {
|
|||
MS_LOG(INFO) << "Save extra profiling data end.";
|
||||
}
|
||||
|
||||
// SaveProfileData function
|
||||
// This function saves the GPU profiling data to the specified path.
|
||||
void GPUProfiler::SaveProfileData() {
|
||||
if (profile_data_path_.empty()) {
|
||||
MS_LOG(WARNING) << "Profile data path is empty, skip save profile data.";
|
||||
} else {
|
||||
// Create a GpuDataSaver instance
|
||||
GpuDataSaver dataSaver(step_trace_op_name_, all_step_start_end_info_);
|
||||
|
||||
// Parse operation information and events for saving
|
||||
dataSaver.ParseOpInfo(op_info_map_);
|
||||
dataSaver.ParseEvent(events_);
|
||||
|
||||
// Write the profile data to the specified path
|
||||
dataSaver.WriteFile(profile_data_path_, base_time_);
|
||||
|
||||
// Save extra profiling data
|
||||
SaveExtraProfileData();
|
||||
}
|
||||
}
|
||||
|
||||
// ClearInst function
|
||||
// This function clears the GPU profiling instance and its associated data.
|
||||
void GPUProfiler::ClearInst() {
|
||||
op_info_map_.clear();
|
||||
op_name_map_.clear();
|
||||
|
|
@ -534,6 +677,8 @@ void GPUProfiler::ClearInst() {
|
|||
cupti_activity_events_drop_count_ = 0l;
|
||||
}
|
||||
|
||||
// ActivityAllocBuffer function
|
||||
// This function allocates a buffer for GPU activity records.
|
||||
void CUPTIAPI ActivityAllocBuffer(uint8_t **buffer, size_t *size, size_t *maxNumRecords) {
|
||||
auto gpu_profiler_inst = GPUProfiler::GetInstance();
|
||||
if (gpu_profiler_inst == nullptr) {
|
||||
|
|
@ -543,6 +688,8 @@ void CUPTIAPI ActivityAllocBuffer(uint8_t **buffer, size_t *size, size_t *maxNum
|
|||
gpu_profiler_inst->AllocBuffer(buffer, size, maxNumRecords);
|
||||
}
|
||||
|
||||
// ActivityProcessBuffer function
|
||||
// This function processes the GPU activity buffer and records.
|
||||
void CUPTIAPI ActivityProcessBuffer(CUcontext ctx, uint32_t streamId, uint8_t *buffer, size_t size, size_t validSize) {
|
||||
PROFILER_ERROR_IF_NULLPTR(buffer);
|
||||
auto gpu_profiler_inst = GPUProfiler::GetInstance();
|
||||
|
|
@ -553,8 +700,10 @@ void CUPTIAPI ActivityProcessBuffer(CUcontext ctx, uint32_t streamId, uint8_t *b
|
|||
gpu_profiler_inst->ProcessBuffer(ctx, streamId, buffer, size, validSize);
|
||||
}
|
||||
|
||||
void ProcessActivityMemcpyRecord(Event *profilingData, CUpti_Activity *record,
|
||||
CUpti_ActivityMemcpy *cupti_activity_memcpy) {
|
||||
// ProcessActivityMemcpyRecord function
|
||||
// This function processes the CUPTI activity record related to memory copy operations.
|
||||
void ProcessActivityMemcpyRecord(Event *profilingData, CUpti_Activity *record, CUpti_ActivityMemcpy *cupti_activity_memcpy) {
|
||||
// Determine the type of memory copy operation and set the profiling data accordingly
|
||||
switch (cupti_activity_memcpy->copyKind) {
|
||||
case CUPTI_ACTIVITY_MEMCPY_KIND_HTOD:
|
||||
profilingData->activity_type = ActivityType::kMemcpyH2D;
|
||||
|
|
@ -599,6 +748,8 @@ void ProcessActivityMemcpyRecord(Event *profilingData, CUpti_Activity *record,
|
|||
}
|
||||
}
|
||||
|
||||
// HandleActivityMemcpyRecord function
|
||||
// This function handles CUPTI activity records related to memory copy operations.
|
||||
void HandleActivityMemcpyRecord(Event *profilingData, CUpti_Activity *record) {
|
||||
CUpti_ActivityMemcpy *cupti_activity_memcpy = reinterpret_cast<CUpti_ActivityMemcpy *>(record);
|
||||
ProcessActivityMemcpyRecord(profilingData, record, cupti_activity_memcpy);
|
||||
|
|
@ -616,6 +767,8 @@ void HandleActivityMemcpyRecord(Event *profilingData, CUpti_Activity *record) {
|
|||
profilingData->memcpy_info.dst_kind = cupti_activity_memcpy->dstKind;
|
||||
}
|
||||
|
||||
// HandleActivityMemcpy2Record function
|
||||
// This function handles CUPTI activity records related to memory copy operations (P2P).
|
||||
void HandleActivityMemcpy2Record(Event *profilingData, CUpti_Activity *record) {
|
||||
CUpti_ActivityMemcpy2 *memcpyP2P = reinterpret_cast<CUpti_ActivityMemcpy2 *>(record);
|
||||
profilingData->activity_type = ActivityType::kMemcpyP2P;
|
||||
|
|
@ -633,6 +786,8 @@ void HandleActivityMemcpy2Record(Event *profilingData, CUpti_Activity *record) {
|
|||
profilingData->memcpy_info.dst_kind = memcpyP2P->dstKind;
|
||||
}
|
||||
|
||||
// HandleActivityMemsetRecord function
|
||||
// This function handles CUPTI activity records related to memory set operations.
|
||||
void HandleActivityMemsetRecord(Event *profilingData, CUpti_Activity *record) {
|
||||
CUpti_ActivityMemset *cupti_activity_memset = reinterpret_cast<CUpti_ActivityMemset *>(record);
|
||||
profilingData->activity_type = ActivityType::kMemset;
|
||||
|
|
@ -647,19 +802,32 @@ void HandleActivityMemsetRecord(Event *profilingData, CUpti_Activity *record) {
|
|||
profilingData->memcpy_info.bytes = cupti_activity_memset->bytes;
|
||||
}
|
||||
|
||||
// HandleActivityKernelRecord function
|
||||
// This function handles CUpti_ActivityKernel4 records and populates profilingData with relevant information.
|
||||
void HandleActivityKernelRecord(Event *profilingData, CUpti_Activity *record) {
|
||||
// Cast the CUpti_Activity record to CUpti_ActivityKernel4 type
|
||||
CUpti_ActivityKernel4 *kernel = reinterpret_cast<CUpti_ActivityKernel4 *>(record);
|
||||
|
||||
// Set the activity type and API type in profilingData
|
||||
profilingData->activity_type = ActivityType::kKernel;
|
||||
profilingData->api_type = CUPTIApiType::kActivity;
|
||||
|
||||
// Get the kernel name and update it with the simplified name
|
||||
profilingData->kernel_name = GetKernelFunc(kernel->name);
|
||||
profilingData->kernel_name = GetKernelFuncName(profilingData->kernel_name);
|
||||
|
||||
// Set the kernel type and timestamps
|
||||
profilingData->kernel_type = "cuLaunchKernel";
|
||||
profilingData->start_time_stamp = kernel->start;
|
||||
profilingData->end_time_stamp = kernel->end;
|
||||
|
||||
// Set the device, context, stream, and correlation IDs
|
||||
profilingData->device_id = kernel->deviceId;
|
||||
profilingData->context_id = kernel->contextId;
|
||||
profilingData->stream_id = kernel->streamId;
|
||||
profilingData->correlation_id = kernel->correlationId;
|
||||
|
||||
// Populate kernel information
|
||||
profilingData->kernel_info.registers_per_thread = kernel->registersPerThread;
|
||||
profilingData->kernel_info.static_shared_memory = kernel->staticSharedMemory;
|
||||
profilingData->kernel_info.dynamic_shared_memory = kernel->dynamicSharedMemory;
|
||||
|
|
@ -671,10 +839,17 @@ void HandleActivityKernelRecord(Event *profilingData, CUpti_Activity *record) {
|
|||
profilingData->kernel_info.grid_z = kernel->gridZ;
|
||||
}
|
||||
|
||||
// GPUProfiler::HandleActivityRecord function
|
||||
// This function processes different types of CUpti_Activity records and delegates to specific record handlers.
|
||||
void GPUProfiler::HandleActivityRecord(CUpti_Activity *record) {
|
||||
// Check for null record pointer
|
||||
PROFILER_ERROR_IF_NULLPTR(record);
|
||||
|
||||
// Create an Event object for profiling data
|
||||
Event profilingData;
|
||||
profilingData.cb_id = 0;
|
||||
|
||||
// Determine the type of the CUpti_Activity record and call the appropriate handler
|
||||
switch (record->kind) {
|
||||
case CUPTI_ACTIVITY_KIND_MEMCPY: {
|
||||
HandleActivityMemcpyRecord(&profilingData, record);
|
||||
|
|
@ -698,48 +873,75 @@ void GPUProfiler::HandleActivityRecord(CUpti_Activity *record) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Add the profilingData event to the profiler's data collection
|
||||
AddEvent(std::move(profilingData));
|
||||
}
|
||||
|
||||
// GPUProfiler::SetStepTraceOpName function
|
||||
// This function sets the step_trace_op_name_ field in the GPUProfiler.
|
||||
void GPUProfiler::SetStepTraceOpName(ProfilingTraceInfo trace_op_name) { step_trace_op_name_ = trace_op_name; }
|
||||
|
||||
// GPUProfiler::RegisterProfilingOp function
|
||||
// This function registers a profiling operation in the profiler.
|
||||
void GPUProfiler::RegisterProfilingOp(std::shared_ptr<ProfilingOp> node) {
|
||||
// Check for null pointer and existing registration
|
||||
PROFILER_ERROR_IF_NULLPTR(node);
|
||||
if (profiling_op_.find(node->Name()) != profiling_op_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the profiling operation
|
||||
node->Init();
|
||||
|
||||
// Register the profiling operation
|
||||
profiling_op_[node->Name()] = node;
|
||||
}
|
||||
|
||||
// GPUProfiler::AllocBuffer function
|
||||
// This function allocates memory for the GPU activity buffer.
|
||||
void CUPTIAPI GPUProfiler::AllocBuffer(uint8_t **buffer, size_t *size, size_t *maxNumRecords) {
|
||||
// Check for null size and maxNumRecords pointers
|
||||
PROFILER_ERROR_IF_NULLPTR(size);
|
||||
PROFILER_ERROR_IF_NULLPTR(maxNumRecords);
|
||||
|
||||
// Allocate memory for the activity buffer
|
||||
int stat = posix_memalign(reinterpret_cast<void **>(buffer), ALIGN_SIZE, BUF_SIZE);
|
||||
if (stat) {
|
||||
MS_LOG(ERROR) << "Out of memory, activity buffer alloc failed.";
|
||||
MS_LOG(ERROR) << "Out of memory, activity buffer allocation failed.";
|
||||
return;
|
||||
}
|
||||
MS_LOG(DEBUG) << "Alloc activity buffer, buffer size: " << BUF_SIZE;
|
||||
|
||||
// Log the allocation size
|
||||
MS_LOG(DEBUG) << "Allocated activity buffer, buffer size: " << BUF_SIZE;
|
||||
|
||||
// Set size and maxNumRecords values
|
||||
*size = BUF_SIZE;
|
||||
*maxNumRecords = 0;
|
||||
}
|
||||
|
||||
// GPUProfiler::ProcessBuffer function
|
||||
// This function processes the GPU activity buffer and handles CUpti_Activity records.
|
||||
void CUPTIAPI GPUProfiler::ProcessBuffer(CUcontext ctx, uint32_t streamId, uint8_t *buffer, size_t size,
|
||||
size_t validSize) {
|
||||
// Skip processing if profiling is not enabled
|
||||
if (!enable_flag_) {
|
||||
MS_LOG(DEBUG) << "Profiler is not enable, skip to process activity record.";
|
||||
MS_LOG(DEBUG) << "Profiler is not enabled, skipping processing of activity record.";
|
||||
free(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
CUptiResult status;
|
||||
CUpti_Activity *record = NULL;
|
||||
|
||||
MS_LOG(DEBUG) << "Process activity buffer, valid size:" << validSize << ",Stream ID:" << streamId;
|
||||
// Log processing information
|
||||
MS_LOG(DEBUG) << "Processing activity buffer, valid size: " << validSize << ", Stream ID: " << streamId;
|
||||
|
||||
// Process each CUpti_Activity record in the buffer
|
||||
if (validSize > 0) {
|
||||
do {
|
||||
status = CuptiActivityGetNextRecord(buffer, validSize, &record);
|
||||
if (status == CUPTI_SUCCESS) {
|
||||
// Handle the current record
|
||||
HandleActivityRecord(record);
|
||||
} else if (status == CUPTI_ERROR_MAX_LIMIT_REACHED) {
|
||||
break;
|
||||
|
|
@ -748,7 +950,7 @@ void CUPTIAPI GPUProfiler::ProcessBuffer(CUcontext ctx, uint32_t streamId, uint8
|
|||
}
|
||||
} while (1);
|
||||
|
||||
// report any records dropped from the queue
|
||||
// Report any records dropped from the queue
|
||||
size_t dropped;
|
||||
CHECK_CUPTI_RET_WITH_ERROR(CuptiActivityGetNumDroppedRecords(ctx, streamId, &dropped),
|
||||
"CuptiActivityGetNumDroppedRecords");
|
||||
|
|
@ -757,18 +959,20 @@ void CUPTIAPI GPUProfiler::ProcessBuffer(CUcontext ctx, uint32_t streamId, uint8
|
|||
}
|
||||
}
|
||||
|
||||
// Free the allocated buffer memory
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
// PYBIND registration for GPUProfiler class
|
||||
REGISTER_PYBIND_DEFINE(GPUProfiler_, ([](const py::module *m) {
|
||||
(void)py::class_<GPUProfiler, std::shared_ptr<GPUProfiler>>(*m, "GPUProfiler")
|
||||
.def_static("get_instance", &GPUProfiler::GetInstance, "GPUProfiler get_instance.")
|
||||
.def("init", &GPUProfiler::Init, py::arg("profile_data_path"), "init")
|
||||
.def("stop", &GPUProfiler::Stop, "stop")
|
||||
.def("init", &GPUProfiler::Init, py::arg("profile_data_path"), "Initialize GPUProfiler")
|
||||
.def("stop", &GPUProfiler::Stop, "Stop GPUProfiler")
|
||||
.def("step_profiling_enable", &GPUProfiler::StepProfilingEnable, py::arg("enable_flag"),
|
||||
"enable or disable step profiling")
|
||||
"Enable or disable step profiling")
|
||||
.def("sync_enable", &GPUProfiler::SyncEnable, py::arg("enable_flag"),
|
||||
"enable or disable synchronization profiling");
|
||||
"Enable or disable synchronization profiling");
|
||||
}));
|
||||
} // namespace gpu
|
||||
} // namespace profiler
|
||||
|
|
|
|||
|
|
@ -25,34 +25,50 @@
|
|||
namespace mindspore {
|
||||
namespace profiler {
|
||||
namespace gpu {
|
||||
// Constants for environment variable names
|
||||
constexpr char kFpStartNode[] = "PROFILING_FP_START";
|
||||
constexpr char kBpEndNode[] = "PROFILING_BP_END";
|
||||
constexpr char kIterEndNode[] = "PROFILING_ITER_END";
|
||||
constexpr auto kInitDatasetQueueOpName = "InitDataSetQueue";
|
||||
|
||||
// Static member initialization
|
||||
bool ProfilingUtils::have_communication_op = false;
|
||||
ProfilingTraceInfo ProfilingUtils::profiling_trace = {"", "", ""};
|
||||
std::unordered_map<uint32_t, bool> ProfilingUtils::is_first_step_map_ = {};
|
||||
|
||||
// GetProfilingTraceFromEnv function
|
||||
// This function extracts profiling trace information from the environment variables and the given graph.
|
||||
ProfilingTraceInfo ProfilingUtils::GetProfilingTraceFromEnv(NotNull<const session::KernelGraph *> graph_ptr) {
|
||||
MS_LOG(INFO) << "get current subgraph op name start.";
|
||||
// Log a message to indicate the start of the process
|
||||
MS_LOG(INFO) << "Getting current subgraph op names start.";
|
||||
|
||||
// Get the execution order of CNodes in the graph
|
||||
auto &cnode_exec_order = graph_ptr->execution_order();
|
||||
|
||||
// Check if there are no CNodes in the execution order
|
||||
if (cnode_exec_order.empty()) {
|
||||
// Return an empty profiling trace
|
||||
return profiling_trace;
|
||||
}
|
||||
|
||||
// Initialize variables
|
||||
ProfilingTraceInfo empty_info;
|
||||
ProfilingTraceInfo last_graph_profiling_trace = profiling_trace;
|
||||
profiling_trace = empty_info;
|
||||
|
||||
// Step 1: Extract profiling trace information from environment variables and the graph
|
||||
SetTraceIterEnd(cnode_exec_order);
|
||||
SetTraceFpStart(cnode_exec_order);
|
||||
SetTraceBpEnd(cnode_exec_order);
|
||||
GetTraceHccl(cnode_exec_order);
|
||||
|
||||
|
||||
// Step 2: Output step trace op name status
|
||||
OutputStepTraceOpNameStatus();
|
||||
|
||||
// Step 3: Update the map to indicate that the current graph has been processed
|
||||
is_first_step_map_[graph_ptr->graph_id()] = false;
|
||||
|
||||
// If current graph has only one node, the bp_end will be empty, so select the last graph node.
|
||||
// If the current graph has only one node, the bp_end will be empty, so select the last graph node.
|
||||
if (profiling_trace.trace_bp_end != "") {
|
||||
return profiling_trace;
|
||||
} else {
|
||||
|
|
@ -60,19 +76,34 @@ ProfilingTraceInfo ProfilingUtils::GetProfilingTraceFromEnv(NotNull<const sessio
|
|||
}
|
||||
}
|
||||
|
||||
// OutputStepTraceOpNameStatus function
|
||||
// This function logs the step trace op names and their statuses.
|
||||
void ProfilingUtils::OutputStepTraceOpNameStatus() {
|
||||
if (profiling_trace.IsValid()) {
|
||||
MS_LOG(INFO) << "Get all the step_trace op name.";
|
||||
// Log a message indicating that step trace op names are being obtained
|
||||
MS_LOG(INFO) << "Getting all the step_trace op names.";
|
||||
}
|
||||
|
||||
// Log the collected profiling trace information
|
||||
MS_LOG(INFO) << "[profiling]trace_fp_start: " << profiling_trace.trace_fp_start
|
||||
<< "trace_bp_end: " << profiling_trace.trace_bp_end
|
||||
<< "trace_iter_end: " << profiling_trace.trace_iter_end;
|
||||
MS_LOG(INFO) << "get step_trace op name end.";
|
||||
|
||||
// Log a message indicating the completion of step trace op name collection
|
||||
MS_LOG(INFO) << "Getting step_trace op names end.";
|
||||
}
|
||||
|
||||
// GetTraceHccl function
|
||||
// This function extracts HCCL communication node names from the given list of CNodes.
|
||||
void ProfilingUtils::GetTraceHccl(const std::vector<CNodePtr> &cnode_exec_order) {
|
||||
// Iterate through the CNodes in the execution order
|
||||
for (const auto &node : cnode_exec_order) {
|
||||
// Check if the current node is a communication op
|
||||
if (common::AnfAlgo::IsCommunicationOp(node)) {
|
||||
// Log the name of the HCCL node found
|
||||
MS_LOG(INFO) << "[profiling]Get hccl node:" << node->fullname_with_scope();
|
||||
|
||||
// Add the HCCL node name to the list of custom nodes in the profiling trace
|
||||
MS_EXCEPTION_IF_NULL(node);
|
||||
if (std::find(profiling_trace.trace_custom_node.begin(), profiling_trace.trace_custom_node.end(),
|
||||
node->fullname_with_scope()) == profiling_trace.trace_custom_node.end()) {
|
||||
|
|
@ -83,42 +114,60 @@ void ProfilingUtils::GetTraceHccl(const std::vector<CNodePtr> &cnode_exec_order)
|
|||
}
|
||||
}
|
||||
|
||||
// SetTraceFpStart function
|
||||
// This function sets the trace_fp_start field in the profiling trace based on environment variables and the first CNode.
|
||||
void ProfilingUtils::SetTraceFpStart(const std::vector<CNodePtr> &cnode_exec_order) {
|
||||
// Step 1: Check if the trace_fp_start environment variable is set
|
||||
const char *trace_fp_start = std::getenv(kFpStartNode);
|
||||
if (trace_fp_start != nullptr) {
|
||||
// Set the trace_fp_start field from the environment variable
|
||||
profiling_trace.trace_fp_start = std::string(trace_fp_start);
|
||||
MS_LOG(INFO) << "Set the Fp Start Op Name from Environment Variable:" << profiling_trace.trace_fp_start;
|
||||
// Log the source of the trace_fp_start value
|
||||
MS_LOG(INFO) << "Setting the Fp Start Op Name from Environment Variable:" << profiling_trace.trace_fp_start;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Determine the trace_fp_start based on CNode order
|
||||
auto first_node = cnode_exec_order.front();
|
||||
MS_EXCEPTION_IF_NULL(first_node);
|
||||
auto node_name = common::AnfAlgo::GetCNodeName(first_node);
|
||||
|
||||
// If the first node is an "InitDataSetQueue" operation, skip it
|
||||
if (node_name == kInitDatasetQueueOpName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the first node is "GetNext", check the node behind it
|
||||
if (node_name == kGetNextOpName) {
|
||||
if (cnode_exec_order.size() > 1) {
|
||||
profiling_trace.trace_fp_start = cnode_exec_order.at(1)->fullname_with_scope();
|
||||
} else {
|
||||
// Log a warning if there is no operation behind "GetNext"
|
||||
MS_LOG(WARNING) << "No Op Behind the GetNext Op" << std::endl;
|
||||
}
|
||||
} else {
|
||||
// Set the trace_fp_start to the name of the first CNode
|
||||
profiling_trace.trace_fp_start = first_node->fullname_with_scope();
|
||||
}
|
||||
}
|
||||
|
||||
// SetTraceBpEnd function
|
||||
// This function sets the trace_bp_end field in the profiling trace based on environment variables and CNodes.
|
||||
void ProfilingUtils::SetTraceBpEnd(const std::vector<CNodePtr> &cnode_exec_order) {
|
||||
// Step 1: Check if the trace_bp_end environment variable is set
|
||||
const char *trace_bp_end = std::getenv(kBpEndNode);
|
||||
if (trace_bp_end != nullptr) {
|
||||
// Set the trace_bp_end field from the environment variable
|
||||
profiling_trace.trace_bp_end = std::string(trace_bp_end);
|
||||
MS_LOG(INFO) << "Set the Bp End Op Name from Environment Variable:" << profiling_trace.trace_bp_end;
|
||||
// Log the source of the trace_bp_end value
|
||||
MS_LOG(INFO) << "Setting the Bp End Op Name from Environment Variable:" << profiling_trace.trace_bp_end;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Determine the trace_bp_end based on CNode order and communication ops
|
||||
std::string bp_end_str;
|
||||
// Contain hccl kernel (try to find the last communication op)
|
||||
|
||||
// Look for the last communication op in the reverse order of execution
|
||||
auto iter = cnode_exec_order.rbegin();
|
||||
while (iter != cnode_exec_order.rend()) {
|
||||
if (common::AnfAlgo::IsCommunicationOp(*iter)) {
|
||||
|
|
@ -126,9 +175,10 @@ void ProfilingUtils::SetTraceBpEnd(const std::vector<CNodePtr> &cnode_exec_order
|
|||
}
|
||||
++iter;
|
||||
}
|
||||
// If find the communication op
|
||||
|
||||
// If a communication op is found, store its input nodes' names
|
||||
if (iter != cnode_exec_order.rend()) {
|
||||
// store communication op input nodes' name
|
||||
// Store communication op input nodes' names
|
||||
std::set<std::string> ar_input_node_names;
|
||||
size_t input_num = common::AnfAlgo::GetInputTensorNum(*iter);
|
||||
for (size_t i = 0; i < input_num; ++i) {
|
||||
|
|
@ -136,9 +186,9 @@ void ProfilingUtils::SetTraceBpEnd(const std::vector<CNodePtr> &cnode_exec_order
|
|||
auto input_node = input_node_with_index.first;
|
||||
ar_input_node_names.insert(input_node->fullname_with_scope());
|
||||
}
|
||||
// start from previous node
|
||||
// Start searching from the previous node
|
||||
++iter;
|
||||
// find input names in previous node
|
||||
// Find input names in previous nodes
|
||||
while (iter != cnode_exec_order.rend()) {
|
||||
if (ar_input_node_names.find((*iter)->fullname_with_scope()) != ar_input_node_names.end()) {
|
||||
bp_end_str = (*iter)->fullname_with_scope();
|
||||
|
|
@ -148,27 +198,37 @@ void ProfilingUtils::SetTraceBpEnd(const std::vector<CNodePtr> &cnode_exec_order
|
|||
}
|
||||
}
|
||||
|
||||
// If bp_end_str is still empty and there are no communication ops, try to find the second last kernel name.
|
||||
if (bp_end_str.empty() && !have_communication_op) {
|
||||
bp_end_str = GetGraphSecondLastKernelName(cnode_exec_order);
|
||||
}
|
||||
|
||||
// Set the trace_bp_end field to the determined value
|
||||
if (!bp_end_str.empty()) {
|
||||
profiling_trace.trace_bp_end = bp_end_str;
|
||||
}
|
||||
}
|
||||
|
||||
// SetTraceIterEnd function
|
||||
// This function sets the trace_iter_end field in the profiling trace based on environment variables and CNodes.
|
||||
void ProfilingUtils::SetTraceIterEnd(const std::vector<CNodePtr> &cnode_exec_order) {
|
||||
// Step 1: Check if the trace_iter_end environment variable is set
|
||||
const char *trace_iter_end = std::getenv(kIterEndNode);
|
||||
if (trace_iter_end != nullptr) {
|
||||
// Set the trace_iter_end field from the environment variable
|
||||
profiling_trace.trace_iter_end = std::string(trace_iter_end);
|
||||
MS_LOG(INFO) << "Set the Iter End Op Name from Environment Variable:" << profiling_trace.trace_iter_end;
|
||||
// Log the source of the trace_iter_end value
|
||||
MS_LOG(INFO) << "Setting the Iter End Op Name from Environment Variable:" << profiling_trace.trace_iter_end;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Determine the trace_iter_end based on CNode order
|
||||
auto iter_end = cnode_exec_order.rbegin();
|
||||
profiling_trace.trace_iter_end = (*iter_end)->fullname_with_scope();
|
||||
}
|
||||
|
||||
// GetGraphSecondLastKernelName function
|
||||
// This function returns the name of the second last kernel in the given list of CNodes.
|
||||
std::string ProfilingUtils::GetGraphSecondLastKernelName(const std::vector<CNodePtr> &cnode_exec_order) {
|
||||
std::string second_last_kernel_name;
|
||||
auto iter = cnode_exec_order.rbegin();
|
||||
|
|
@ -180,6 +240,8 @@ std::string ProfilingUtils::GetGraphSecondLastKernelName(const std::vector<CNode
|
|||
return second_last_kernel_name;
|
||||
}
|
||||
|
||||
// IsFirstStep function
|
||||
// This function checks if the graph with the given graph_id is the first step in profiling.
|
||||
bool ProfilingUtils::IsFirstStep(const uint32_t graph_id) {
|
||||
auto iter = is_first_step_map_.find(graph_id);
|
||||
if (iter == is_first_step_map_.end()) {
|
||||
|
|
|
|||
|
|
@ -43,30 +43,93 @@ struct ProfilingTraceInfo {
|
|||
bool IsValid() const { return !(trace_fp_start.empty() || trace_bp_end.empty() || trace_iter_end.empty()); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A utility class for handling profiling trace points and information.
|
||||
*/
|
||||
class ProfilingUtils {
|
||||
public:
|
||||
ProfilingUtils() = default;
|
||||
~ProfilingUtils() = default;
|
||||
|
||||
// Get profiling trace point from envs.
|
||||
// export PROFILING_FP_START='full name of the first cnode to execute'
|
||||
// export PROFILING_BP_END='full name of the last backpropagation cnode to execute'
|
||||
// export PROFILING_ITER_END='full name of last cnode in graph to execute'
|
||||
/**
|
||||
* @brief Get profiling trace points from environment variables.
|
||||
*
|
||||
* This function retrieves profiling trace points from environment variables.
|
||||
* It looks for specific environment variables and extracts trace points information.
|
||||
*
|
||||
* @param graph_ptr Pointer to the kernel graph.
|
||||
* @return ProfilingTraceInfo containing trace point information.
|
||||
*/
|
||||
static ProfilingTraceInfo GetProfilingTraceFromEnv(NotNull<const session::KernelGraph *> graph_ptr);
|
||||
|
||||
/**
|
||||
* @brief Output step trace op name and status.
|
||||
*
|
||||
* This function outputs the op names and their status related to profiling.
|
||||
*/
|
||||
static void OutputStepTraceOpNameStatus();
|
||||
|
||||
/**
|
||||
* @brief Check if the current step is the first step of a graph.
|
||||
*
|
||||
* @param graph_id The identifier of the graph.
|
||||
* @return True if it's the first step, false otherwise.
|
||||
*/
|
||||
static bool IsFirstStep(const uint32_t graph_id);
|
||||
|
||||
/**
|
||||
* @brief A flag indicating whether there are communication ops in the graph.
|
||||
*/
|
||||
static bool have_communication_op;
|
||||
|
||||
/**
|
||||
* @brief Profiling trace information containing trace points.
|
||||
*/
|
||||
static ProfilingTraceInfo profiling_trace;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Set the trace point for the first forward-propagation (FP) operation.
|
||||
*
|
||||
* @param cnode_exec_order The execution order of CNodes in the graph.
|
||||
*/
|
||||
static void SetTraceFpStart(const std::vector<CNodePtr> &cnode_exec_order);
|
||||
|
||||
/**
|
||||
* @brief Set the trace point for the last back-propagation (BP) operation.
|
||||
*
|
||||
* @param cnode_exec_order The execution order of CNodes in the graph.
|
||||
*/
|
||||
static void SetTraceBpEnd(const std::vector<CNodePtr> &cnode_exec_order);
|
||||
|
||||
/**
|
||||
* @brief Set the trace point for the last iteration (ITER) operation.
|
||||
*
|
||||
* @param cnode_exec_order The execution order of CNodes in the graph.
|
||||
*/
|
||||
static void SetTraceIterEnd(const std::vector<CNodePtr> &cnode_exec_order);
|
||||
|
||||
/**
|
||||
* @brief Get the name of the second last kernel in the graph.
|
||||
*
|
||||
* @param cnode_exec_order The execution order of CNodes in the graph.
|
||||
* @return The name of the second last kernel.
|
||||
*/
|
||||
static std::string GetGraphSecondLastKernelName(const std::vector<CNodePtr> &cnode_exec_order);
|
||||
|
||||
/**
|
||||
* @brief Get trace information related to HCCL communication operations.
|
||||
*
|
||||
* @param cnode_exec_order The execution order of CNodes in the graph.
|
||||
*/
|
||||
static void GetTraceHccl(const std::vector<CNodePtr> &cnode_exec_order);
|
||||
|
||||
/**
|
||||
* @brief A map to keep track of whether a graph is the first step.
|
||||
*/
|
||||
static std::unordered_map<uint32_t, bool> is_first_step_map_;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace profiler
|
||||
} // namespace mindspore
|
||||
|
|
|
|||
|
|
@ -1,82 +1,71 @@
|
|||
/**
|
||||
* Copyright 2021 Huawei Technologies Co., Ltd
|
||||
*
|
||||
* 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 "profiler/device/profiling.h"
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include "include/common/pybind_api/api_register.h"
|
||||
#include "utils/log_adapter.h"
|
||||
#include "include/common/utils/utils.h"
|
||||
#if ENABLE_GPU
|
||||
#include "profiler/device/gpu/gpu_profiling.h"
|
||||
#endif
|
||||
#if ENABLE_D
|
||||
#include "profiler/device/ascend/ascend_profiling.h"
|
||||
#endif
|
||||
|
||||
namespace mindspore {
|
||||
namespace profiler {
|
||||
std::shared_ptr<ProfilerManager> ProfilerManager::profiler_manager_inst_ = std::make_shared<ProfilerManager>();
|
||||
|
||||
// Function to get the host monotonic timestamp in microseconds
|
||||
// Returns: uint64_t - Current host monotonic timestamp in microseconds
|
||||
uint64_t Profiler::GetHostMonoTimeStamp() const {
|
||||
struct timespec ts;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// On Windows, use CLOCK_MONOTONIC for timestamp
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
|
||||
MS_LOG(ERROR) << "Get host timestamp failed";
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
// On non-Windows systems, use CLOCK_MONOTONIC_RAW for timestamp
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) != 0) {
|
||||
MS_LOG(ERROR) << "Get host timestamp failed";
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr uint64_t kNSecondInSecond = 1000000000;
|
||||
uint64_t cur_time_stamp = ts.tv_sec * kNSecondInSecond + ts.tv_nsec;
|
||||
return cur_time_stamp;
|
||||
}
|
||||
|
||||
// Function to get the real timestamp in microseconds
|
||||
// Returns: uint64_t - Current real timestamp in microseconds
|
||||
uint64_t Profiler::GetRealTimeStamp() const {
|
||||
struct timeval tv = {0, 0};
|
||||
(void)gettimeofday(&tv, NULL);
|
||||
int64_t kUSecondInSecond = 1000000;
|
||||
int64_t ts = kUSecondInSecond * static_cast<int64_t>(tv.tv_sec);
|
||||
ts += static_cast<int64_t>(tv.tv_usec);
|
||||
// us timestamp
|
||||
// Return the timestamp in microseconds
|
||||
return (uint64_t)ts;
|
||||
}
|
||||
|
||||
// Function to set runtime data for an operation
|
||||
// Parameters:
|
||||
// op_name (const std::string&) - The name of the operation
|
||||
// time_elapsed (const float) - Time elapsed for the operation
|
||||
void Profiler::SetRunTimeData(const std::string &op_name, const float time_elapsed) {
|
||||
std::shared_lock<std::shared_mutex> lock(op_map_mutex_);
|
||||
auto iter = op_info_map_.find(op_name);
|
||||
if (iter != op_info_map_.end()) {
|
||||
// Update the host cost time for the operation
|
||||
iter->second.op_host_cost_time += time_elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to set runtime data with start timestamp and duration for an operation
|
||||
// Parameters:
|
||||
// op_name (const std::string&) - The name of the operation
|
||||
// start (const uint64_t) - Start timestamp of the operation
|
||||
// duration (const float) - Duration of the operation
|
||||
void Profiler::SetRunTimeData(const std::string &op_name, const uint64_t start, const float duration) {
|
||||
std::shared_lock<std::shared_mutex> lock(op_map_mutex_);
|
||||
auto iter = op_info_map_.find(op_name);
|
||||
if (iter != op_info_map_.end()) {
|
||||
// Add start and duration information for the operation
|
||||
iter->second.start_duration.emplace_back(StartDuration({start, duration}));
|
||||
}
|
||||
}
|
||||
|
||||
// Function to record start and end information for one step
|
||||
// Explanation: This function records the start and end information for one step of the computation graph.
|
||||
void Profiler::RecordOneStepStartEndInfo() {
|
||||
// Multi-graph dotting data is not supported.
|
||||
std::lock_guard<std::mutex> locker(record_mutex_);
|
||||
|
|
@ -86,7 +75,7 @@ void Profiler::RecordOneStepStartEndInfo() {
|
|||
step_start_end_info_.iter_start_op_name = step_start_end_info_vector_[0];
|
||||
step_start_end_info_.fp_start_op_name = step_start_end_info_vector_[0];
|
||||
|
||||
// If is the first step, the step_start_end_info_vector_ length is 1.
|
||||
// If it is the first step, the step_start_end_info_vector_ length is 1.
|
||||
if (vector_size > 1) {
|
||||
// Iterate through step_start_end_info_vector_ for the repeat operator, which is the operator of the next step and
|
||||
// is preceded by iter_end_op of the current step.
|
||||
|
|
@ -138,6 +127,9 @@ void Profiler::RecordOneStepStartEndInfo() {
|
|||
step_start_end_info_.fp_start_op_name = "";
|
||||
}
|
||||
|
||||
// Function to record start and end information for one step of an operation
|
||||
// Parameters:
|
||||
// op_name (const std::string&) - The name of the operation
|
||||
void Profiler::RecordOneStepStartEndInfo(const std::string op_name) {
|
||||
std::lock_guard<std::mutex> locker(record_mutex_);
|
||||
if (step_start_end_info_.iter_start_op_name.empty()) {
|
||||
|
|
@ -157,11 +149,15 @@ void Profiler::RecordOneStepStartEndInfo(const std::string op_name) {
|
|||
step_start_end_info_vector_.push_back(op_name);
|
||||
}
|
||||
|
||||
// Get the singleton instance of ProfilerManager
|
||||
// Returns: std::shared_ptr<ProfilerManager> - Shared pointer to the ProfilerManager instance
|
||||
std::shared_ptr<ProfilerManager> &ProfilerManager::GetInstance() {
|
||||
MS_EXCEPTION_IF_NULL(profiler_manager_inst_);
|
||||
return profiler_manager_inst_;
|
||||
}
|
||||
|
||||
// Get the profiling enable flag
|
||||
// Returns: bool - Profiling enable flag
|
||||
bool ProfilerManager::GetProfilingEnableFlag() const {
|
||||
#if ENABLE_GPU
|
||||
return profiler::gpu::GPUProfiler::GetInstance()->GetEnableFlag();
|
||||
|
|
@ -174,6 +170,7 @@ bool ProfilerManager::GetProfilingEnableFlag() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Record start and end information for one step (if GPU profiling is enabled)
|
||||
void ProfilerManager::RecordOneStepStartEndInfo() const {
|
||||
#if ENABLE_GPU
|
||||
auto gpu_profiler_inst = profiler::gpu::GPUProfiler::GetInstance();
|
||||
|
|
@ -183,6 +180,8 @@ void ProfilerManager::RecordOneStepStartEndInfo() const {
|
|||
#endif
|
||||
}
|
||||
|
||||
// Get profiling options as a string (if Ascend profiling is enabled)
|
||||
// Returns: std::string - Profiling options as a string
|
||||
std::string ProfilerManager::GetProfilingOptions() const {
|
||||
#if ENABLE_D
|
||||
auto ascend_instance = profiler::ascend::AscendProfiler::GetInstance();
|
||||
|
|
|
|||
|
|
@ -1,21 +1,11 @@
|
|||
/**
|
||||
* Copyright 2021 Huawei Technologies Co., Ltd
|
||||
*
|
||||
* 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.
|
||||
* @file device_profiling.h
|
||||
* @brief This header file contains declarations of classes and structures related to device profiling.
|
||||
*/
|
||||
|
||||
#ifndef MINDSPORE_CCSRC_PROFILER_DEVICE_PROFILING_H
|
||||
#define MINDSPORE_CCSRC_PROFILER_DEVICE_PROFILING_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
|
|
@ -30,32 +20,37 @@
|
|||
|
||||
namespace mindspore {
|
||||
namespace profiler {
|
||||
|
||||
// Structure to store start timestamp, duration, and thread ID of an event
|
||||
struct StartDuration {
|
||||
uint64_t start_timestamp = 0l;
|
||||
float duration = 0l;
|
||||
size_t tid = 0;
|
||||
uint64_t start_timestamp = 0l; /**< Start timestamp of the event in microseconds. */
|
||||
float duration = 0l; /**< Duration of the event in microseconds. */
|
||||
size_t tid = 0; /**< Thread ID associated with the event. */
|
||||
};
|
||||
|
||||
// Structure to store start and end operation names for one step
|
||||
struct OneStepStartEndInfo {
|
||||
std::string iter_start_op_name;
|
||||
std::string fp_start_op_name;
|
||||
std::string iter_end_op_name;
|
||||
std::string iter_start_op_name; /**< Operation name for iteration start. */
|
||||
std::string fp_start_op_name; /**< Operation name for forward propagation start. */
|
||||
std::string iter_end_op_name; /**< Operation name for iteration end. */
|
||||
};
|
||||
|
||||
// Structure to store profiling information for an operation
|
||||
struct OpInfo {
|
||||
std::string op_name;
|
||||
float cupti_api_call_time = 0l;
|
||||
float cupti_activity_time = 0l;
|
||||
float op_host_cost_time = 0;
|
||||
int op_kernel_api_count = 0;
|
||||
int op_kernel_count = 0;
|
||||
int op_count = 0;
|
||||
StartDuration tmp_start_duration;
|
||||
std::vector<StartDuration> start_duration;
|
||||
void *stream;
|
||||
uint32_t pid;
|
||||
std::string op_name; /**< Name of the operation. */
|
||||
float cupti_api_call_time = 0l; /**< Time spent on CUPTI API calls for the operation in microseconds. */
|
||||
float cupti_activity_time = 0l; /**< Time spent on CUPTI activities for the operation in microseconds. */
|
||||
float op_host_cost_time = 0; /**< Host-side cost time for the operation in microseconds. */
|
||||
int op_kernel_api_count = 0; /**< Number of kernel API calls for the operation. */
|
||||
int op_kernel_count = 0; /**< Number of kernels launched for the operation. */
|
||||
int op_count = 0; /**< Number of times the operation was executed. */
|
||||
StartDuration tmp_start_duration; /**< Temporary start duration for the operation. */
|
||||
std::vector<StartDuration> start_duration; /**< Vector of start durations for the operation. */
|
||||
void *stream; /**< Stream associated with the operation. */
|
||||
uint32_t pid; /**< Process ID associated with the operation. */
|
||||
};
|
||||
|
||||
// Class to manage profiler settings and options
|
||||
class ProfilerManager {
|
||||
public:
|
||||
static std::shared_ptr<ProfilerManager> &GetInstance();
|
||||
|
|
@ -63,53 +58,49 @@ class ProfilerManager {
|
|||
~ProfilerManager() = default;
|
||||
ProfilerManager(const ProfilerManager &) = delete;
|
||||
ProfilerManager &operator=(const ProfilerManager &) = delete;
|
||||
bool GetProfilingEnableFlag() const;
|
||||
void RecordOneStepStartEndInfo() const;
|
||||
std::string GetProfilingOptions() const;
|
||||
|
||||
private:
|
||||
static std::shared_ptr<ProfilerManager> profiler_manager_inst_;
|
||||
bool GetProfilingEnableFlag() const; /**< Get the profiling enable flag. */
|
||||
void RecordOneStepStartEndInfo() const; /**< Record one step start and end information. */
|
||||
std::string GetProfilingOptions() const; /**< Get profiling options as a string. */
|
||||
};
|
||||
|
||||
// Abstract base class for profiling
|
||||
class Profiler {
|
||||
public:
|
||||
Profiler() = default;
|
||||
virtual ~Profiler() = default;
|
||||
|
||||
virtual void Init(const std::string &profileDataPath) = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void StepProfilingEnable(const bool enable_flag) = 0;
|
||||
virtual void OpDataProducerEnd() = 0;
|
||||
void RecordOneStepStartEndInfo();
|
||||
bool GetEnableFlag() const { return enable_flag_; }
|
||||
std::string ProfileDataPath() const { return profile_data_path_; }
|
||||
void RecordOneStepStartEndInfo(std::string op_name);
|
||||
std::pair<double, double> GetSingleOpLaunchTime() { return single_op_launch_start_time_end_time_; }
|
||||
void SetSingleOpLaunchTime(const std::pair<double, double> &launch_start_end) {
|
||||
single_op_launch_start_time_end_time_ = launch_start_end;
|
||||
}
|
||||
virtual void Init(const std::string &profileDataPath) = 0; /**< Initialize the profiler. */
|
||||
virtual void Stop() = 0; /**< Stop the profiler. */
|
||||
virtual void StepProfilingEnable(const bool enable_flag) = 0; /**< Enable or disable step profiling. */
|
||||
virtual void OpDataProducerEnd() = 0; /**< Notify the end of operation data production. */
|
||||
void RecordOneStepStartEndInfo(); /**< Record start and end information for one step. */
|
||||
bool GetEnableFlag() const; /**< Get the enable flag for profiling. */
|
||||
std::string ProfileDataPath() const; /**< Get the path for profile data storage. */
|
||||
void RecordOneStepStartEndInfo(std::string op_name); /**< Record start and end information for one step of an operation. */
|
||||
std::pair<double, double> GetSingleOpLaunchTime(); /**< Get the start and end time of a single operation launch. */
|
||||
void SetSingleOpLaunchTime(const std::pair<double, double> &launch_start_end); /**< Set the start and end time of a single operation launch. */
|
||||
|
||||
protected:
|
||||
void SetRunTimeData(const std::string &op_name, const float time_elapsed);
|
||||
void SetRunTimeData(const std::string &op_name, const uint64_t start, const float duration);
|
||||
uint64_t GetHostMonoTimeStamp() const;
|
||||
// Get timestamp in us
|
||||
uint64_t GetRealTimeStamp() const;
|
||||
virtual void SaveProfileData() = 0;
|
||||
virtual void ClearInst() = 0;
|
||||
std::pair<double, double> single_op_launch_start_time_end_time_;
|
||||
bool enable_flag_ = false;
|
||||
bool has_find = false;
|
||||
uint32_t iter_end_op_index = 0;
|
||||
std::string profile_data_path_;
|
||||
std::unordered_map<std::string, OpInfo> op_info_map_;
|
||||
OneStepStartEndInfo step_start_end_info_;
|
||||
std::vector<OneStepStartEndInfo> all_step_start_end_info_;
|
||||
std::vector<std::string> step_start_end_info_vector_;
|
||||
std::shared_mutex op_map_mutex_;
|
||||
std::mutex record_mutex_;
|
||||
bool init_flag_ = false;
|
||||
void SetRunTimeData(const std::string &op_name, const float time_elapsed); /**< Set runtime data for an operation. */
|
||||
void SetRunTimeData(const std::string &op_name, const uint64_t start, const float duration); /**< Set runtime data with start timestamp and duration for an operation. */
|
||||
uint64_t GetHostMonoTimeStamp() const; /**< Get the host monotonic timestamp. */
|
||||
uint64_t GetRealTimeStamp() const; /**< Get the real timestamp in microseconds. */
|
||||
virtual void SaveProfileData() = 0; /**< Save the profile data. */
|
||||
virtual void ClearInst() = 0; /**< Clear the profiler instance. */
|
||||
std::pair<double, double> single_op_launch_start_time_end_time_; /**< Pair of start and end times for a single operation launch. */
|
||||
bool enable_flag_ = false; /**< Flag indicating whether profiling is enabled. */
|
||||
bool has_find = false; /**< Flag indicating whether an operation has been found. */
|
||||
uint32_t iter_end_op_index = 0; /**< Index of iteration end operation. */
|
||||
std::string profile_data_path_; /**< Path for profile data storage. */
|
||||
std::unordered_map<std::string, OpInfo> op_info_map_; /**< Map to store profiling information for operations. */
|
||||
OneStepStartEndInfo step_start_end_info_; /**< Information about the start and end of one step. */
|
||||
std::vector<OneStepStartEndInfo> all_step_start_end_info_; /**< Vector of start and end information for all steps. */
|
||||
std::vector<std::string> step_start_end_info_vector_; /**< Vector of start and end information for steps. */
|
||||
std::shared_mutex op_map_mutex_; /**< Shared mutex for locking operation information map. */
|
||||
std::mutex record_mutex_; /**< Mutex for recording information. */
|
||||
bool init_flag_ = false; /**< Flag indicating whether the profiler has been initialized. */
|
||||
};
|
||||
|
||||
} // namespace profiler
|
||||
} // namespace mindspore
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue