diff --git a/mindspore/python/mindspore/profiler/analysis/parser/ascend_cann_parser.py b/mindspore/python/mindspore/profiler/analysis/parser/ascend_cann_parser.py index b1ff1ba06a1dcb76b33cb69f6c95576d8880e800..b5c000ac8b30c204a60deb125c72f827137c7db6 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/ascend_cann_parser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/ascend_cann_parser.py @@ -14,7 +14,9 @@ # ============================================================================ import os +import csv import glob +import numpy as np from typing import Dict, Optional from mindspore import log as logger @@ -27,6 +29,25 @@ class AscendMsprofParser(BaseParser): """Parser for MindSpore profiling data on Ascend platform.""" _MSPROF_TIMELINE_FILE_PATTERN = "msprof*.json" + _OP_SUMMARY_FILE_PATTERN = "op_summary_*.csv" + _OP_SUMMARY_FIELDS_TYPE = [ + ('Model ID', float), + ('Task ID', int), + ('Stream ID', int), + ('Op Name', object), + ('Op Type', object), + ('Task Type', object), + ('Task Start Time', object), + ('Task Duration', float), + ('Task Wait Time', float), + ('Input Shapes', object), + ('Input Data Types', object), + ('Input Formats', object), + ('Output Shapes', object), + ('Output Data Types', object), + ('Output Formats', object), + ('Task Start Time(us)', object) + ] def __init__(self, next_parser: Optional[BaseParser] = None, **kwargs): super().__init__(next_parser) @@ -47,10 +68,38 @@ class AscendMsprofParser(BaseParser): if data is None: data = {} AscendMsprofExporter(**self._kwargs).export() + self._parse_op_summary() self._parse_msprof_timeline() - data["msprof_timeline"] = self.msprof_timeline + data.update({ + "op_summary": self.op_summary, + "msprof_timeline": self.msprof_timeline + }) return data + def _parse_op_summary(self): + """Parse operation summary data.""" + pattern = os.path.join(self._msprof_profile_output_path, self._MSPROF_TIMELINE_FILE_PATTERN) + file_path_list = glob.glob(pattern) + if not file_path_list: + return + op_summary_list = [] + # Handle possible slice data. + for file_path in file_path_list: + op_summary = self._parse_csv_file(file_path, self._process_op_summary_row) + if op_summary: + op_summary_list.extend(op_summary) + # Determine if vector_fops and "cube_fops" need to be added based on the data length. + if op_summary_list and len(op_summary_list[0]) > len(self._OP_SUMMARY_FIELDS_TYPE): + self._OP_SUMMARY_FIELDS_TYPE.extend([("vector_fops", float), ("cube_fops", float)]) + self.op_summary = np.array(op_summary_list, dtype=self._OP_SUMMARY_FIELDS_TYPE) + self._convert_op_summary_times() + + def _convert_op_summary_times(self): + """Convert operation summary times from microseconds to milliseconds.""" + self.op_summary['Task Start Time(us)'] = self.op_summary['Task Start Time'].copy().astype(float) + for field in ["Task Start Time", "Task Duration", "Task Wait Time"]: + self.op_summary[field] = self.op_summary[field].astype(float) * 1e-3 + def _parse_msprof_timeline(self) -> None: """Parse msprof timeline from JSON files.""" pattern = os.path.join(self._msprof_profile_output_path, self._MSPROF_TIMELINE_FILE_PATTERN) @@ -79,3 +128,41 @@ class AscendMsprofParser(BaseParser): logger.warning(f"Msporf timeline data in {file_path} is empty.") except RuntimeError as err: logger.error(f"Failed to read file {file_path}. Error: {err}") + + @staticmethod + def _parse_csv_file(file_path, row_processor): + """Parse a CSV file using the provided row processor.""" + data = [] + with open(file_path, newline="") as csvfile: + reader = csv.DictReader(csvfile) + for row in reader: + data.append(row_processor(row)) + return data + + @staticmethod + def _process_op_summary_row(row): + """Process a single row of operation summary data.""" + new_row = [ + row.get("Model ID"), + row.get("Task ID"), + row.get("Stream ID"), + row.get("Op Name"), + row.get("OP Type"), + row.get("Task Type"), + row.get("Task Start Time(us)"), + row.get("Task Duration(us)"), + row.get("Task Wait Time(us)"), + row.get("Input Shapes"), + row.get("Input Data Types"), + row.get("Input Formats"), + row.get("Output Shapes"), + row.get("Output Data Types"), + row.get("Output Formats"), + "0.000" # Task Start Time(us) placeholder. + ] + if "vector_fops" in row and "cube_fops" in row: + new_row.extend([row["vector_fops"], row["cube_fops"]]) + elif "aiv_vector_fops" in row and "aic_cube_fops" in row: + new_row.extend([row["aiv_vector_fops"], row["aic_cube_fops"]]) + # Convert 'N/A' to "0" + return tuple("0" if d == "N/A" else d for d in new_row) diff --git a/mindspore/python/mindspore/profiler/analysis/parser/framework_cann_relation_parser.py b/mindspore/python/mindspore/profiler/analysis/parser/framework_cann_relation_parser.py index 06aa2c877045b44d498a1ac8e28f79ee1ddf84ef..696c01914601c79492c5caa65651825816037d53 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/framework_cann_relation_parser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/framework_cann_relation_parser.py @@ -16,7 +16,7 @@ from typing import Dict, Any from mindspore.profiler.analysis.parser.base_parser import BaseParser -from mindspore.profiler.analysis.parser.trace_analyser.ascend_trace_analyser import AscendTraceAnalyser +from mindspore.profiler.analysis.parser.timeline_analyser.ascend_timeline_analyser import AscendTimelineAnalyser class FrameworkCannRelationParser(BaseParser): @@ -25,13 +25,13 @@ class FrameworkCannRelationParser(BaseParser): def __init__(self, **kwargs): """Initialize the AscendTraceAnalyser.""" super().__init__() - self.analyser = AscendTraceAnalyser() + self.analyser = AscendTimelineAnalyser() def _parse(self, data: Dict[str, Any]) -> Dict[str, Any]: """Parse the relation of framework and cann data.""" self.analyser.analyse(data) data.update({ "trace_view": self.analyser.get_trace_view_data(), - "dispatch_kernels": self.analyser.get_fwk_dispatch_device_kernels(), + "trace_event_recorder": self.analyser.get_trace_event_recoder() }) return data diff --git a/mindspore/python/mindspore/profiler/analysis/parser/ms_framework_parser.py b/mindspore/python/mindspore/profiler/analysis/parser/ms_framework_parser.py index ce33683142fb4125c97ab225fd6276563c1f1245..75d10f25665392c697be97885cb81a475ae16c71 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/ms_framework_parser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/ms_framework_parser.py @@ -20,7 +20,7 @@ from mindspore import log as logger from mindspore.profiler.analysis.parser.base_parser import BaseParser from mindspore.profiler.common.file_manager import FileManager from mindspore.profiler.common.tlv_decoder import TLVDecoder -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import MindSporeOpEvent +from mindspore.profiler.analysis.parser.timeline_event.mindspore_op_event import MindSporeOpEvent class FrameworkParser(BaseParser): diff --git a/mindspore/python/mindspore/profiler/analysis/parser/ms_minddata_parser.py b/mindspore/python/mindspore/profiler/analysis/parser/ms_minddata_parser.py index 9f012a7b13fbc1d2ee3accad8cb7e4005d66eabf..435957f200cb8b7de2a9c7700309c9323cff46c8 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/ms_minddata_parser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/ms_minddata_parser.py @@ -48,7 +48,9 @@ class MindDataParser(BaseParser): for key, file_name in self._FILE_NAMES.items() } - def _parse(self, data: Dict[str, Any]) -> Dict[str, Any]: + def _parse(self, data=None) -> Dict[str, Any]: + if data is None: + data = {} op_id_info, sample_interval = self._parse_pipeline_info_dict() cpu_util_info = self._parse_cpu_util_info() device_trace_info = self._parse_device_trace() diff --git a/mindspore/python/mindspore/profiler/analysis/parser/op_range_to_csv.py b/mindspore/python/mindspore/profiler/analysis/parser/op_range_to_csv.py index 8286d20d87ddcda755ecf9434db03ea522d7394e..bee49210d89edd2c85f87fd4d853a2ab8d0a9982 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/op_range_to_csv.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/op_range_to_csv.py @@ -4,7 +4,7 @@ import glob from unittest.mock import patch from mindspore.profiler.common.file_manager import FileManager from mindspore.profiler.common.tlv_decoder import TLVDecoder -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import MindSporeOpEvent, MindSporeOpEnum +from mindspore.profiler.analysis.parser.timeline_event.mindspore_op_event import MindSporeOpEvent, MindSporeOpEnum class OpRangeToCsv: @staticmethod diff --git a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/__init__.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/__init__.py similarity index 100% rename from mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/__init__.py rename to mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/__init__.py diff --git a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ascend_msprof_trace_analyser.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ascend_msprof_timeline_analyser.py similarity index 33% rename from mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ascend_msprof_trace_analyser.py rename to mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ascend_msprof_timeline_analyser.py index 572f99777a4601642c7dae5ca66e3f6eb75f95a6..a3293db5591e9baa5284af09bfde7a45c5da8c20 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ascend_msprof_trace_analyser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ascend_msprof_timeline_analyser.py @@ -15,49 +15,63 @@ from typing import List, Dict -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import CANNEvent, BaseEvent -from mindspore.profiler.analysis.parser.trace_analyser.ms_scope_layer_analyser import is_scope_data -from mindspore.profiler.analysis.parser.trace_analyser.base_trace_analyser import BaseTraceAnalyser +from mindspore.profiler.analysis.parser.timeline_event.msprof_event import MsporfEvent +from mindspore.profiler.analysis.parser.timeline_analyser.ms_scope_layer_analyser import is_scope_data +from mindspore.profiler.analysis.parser.timeline_analyser.base_timeline_analyser import BaseTimelineAnalyser +from mindspore.profiler.analysis.parser.timeline_event.timeline_event_recorder import TimelineProcessEvents -class AscendMsprofTraceAnalyser(BaseTraceAnalyser): +class AscendMsprofTimelineAnalyser(BaseTimelineAnalyser): """Analyser for Ascend MsProf trace data.""" def __init__(self): super().__init__() - self.cann_to_npu_flow_dict: Dict[int, Dict[str, List[CANNEvent]]] = {} - self.scope_data: List[BaseEvent] = [] + self.msprof_timeline_raw_data = None + self.cann_to_npu_flow_dict: Dict[int, Dict[str, List[MsporfEvent]]] = {} + self.scope_data: List[MsporfEvent] = [] def analyse(self, msprof_timeline_data: List[Dict]) -> None: """Analyse MsProf timeline data and generate events.""" if not msprof_timeline_data: return - flow_dict, event_dict = {}, {} + self.msprof_timeline_raw_data = msprof_timeline_data + flow_dict, x_event_dict = {}, {} for data in msprof_timeline_data: - cann_event = CANNEvent(data) - if cann_event.is_flow_start_event(): - flow_dict.setdefault(cann_event.id, {}).setdefault("start", cann_event) - elif cann_event.is_flow_end_event(): - flow_dict.setdefault(cann_event.id, {}).setdefault("end", cann_event) - elif cann_event.is_x_event(): - if is_scope_data(cann_event): - self.scope_data.append(cann_event) - event_dict[cann_event.unique_id] = cann_event + msprof_event = MsporfEvent(data) + pid = msprof_event.pid + if msprof_event.is_flow_start_event(): + flow_dict.setdefault(msprof_event.flow_id, {}).setdefault("start", msprof_event) + elif msprof_event.is_flow_end_event(): + flow_dict.setdefault(msprof_event.flow_id, {}).setdefault("end", msprof_event) + elif msprof_event.is_x_event(): + if is_scope_data(msprof_event): + self.scope_data.append(msprof_event) + x_event_dict[msprof_event.unique_id] = msprof_event + self.process_events.setdefault(pid, TimelineProcessEvents(pid)).add_event(msprof_event) + elif msprof_event.is_i_event(): + self.process_events.setdefault(pid, TimelineProcessEvents(pid)).add_event(msprof_event) + elif msprof_event.is_m_event(): + self.process_events.setdefault(pid, TimelineProcessEvents(pid)).add_metadata(msprof_event.origin_data) + for flow in flow_dict.values(): - start_event = flow.get("start") - end_event = flow.get("end") - if start_event and end_event: - kernel_event = event_dict.get(end_event.unique_id) - if not kernel_event: + flow_start = flow.get("start") + flow_end = flow.get("end") + if flow_start and flow_end: + hardware_event = x_event_dict.get(flow_end.unique_id) + if not hardware_event: continue - (self.cann_to_npu_flow_dict.setdefault(start_event.tid, {}) - .setdefault(str(start_event.ts), []).append(kernel_event)) - self.events = msprof_timeline_data + (self.cann_to_npu_flow_dict.setdefault(flow_start.tid, {}). + setdefault(str(flow_start.ts), []).append(hardware_event)) + self.process_events[hardware_event.pid].add_cross_pid_flow_event(hardware_event) - def get_cann_to_npu_flow(self) -> Dict[int, Dict[str, List[CANNEvent]]]: + def get_cann_to_npu_flow(self) -> Dict[int, Dict[str, List[MsporfEvent]]]: """Return the CANN to NPU flow dictionary.""" return self.cann_to_npu_flow_dict - def get_scope_data(self) -> List[BaseEvent]: + def get_scope_data(self) -> List[MsporfEvent]: """Return the scope data.""" return self.scope_data + + def get_chrome_trace_events(self) -> List[Dict]: + """Return the chrome trace events.""" + return self.msprof_timeline_raw_data diff --git a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ascend_trace_analyser.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ascend_timeline_analyser.py similarity index 61% rename from mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ascend_trace_analyser.py rename to mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ascend_timeline_analyser.py index 181204af5c754e3b4f8b681ac96ce85d02122c8d..5bbbb53db6134fe7949184d316f132724da7a935 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ascend_trace_analyser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ascend_timeline_analyser.py @@ -17,32 +17,37 @@ from typing import List, Dict, Any from decimal import Decimal from mindspore import log as logger -from mindspore.profiler.analysis.parser.trace_analyser.trace_event_manager import TraceEventManager -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import CANNEvent, MindSporeOpEvent, BaseEvent -from mindspore.profiler.analysis.parser.trace_analyser.ms_cpu_op_analyser import CpuOpAnalyser -from mindspore.profiler.analysis.parser.trace_analyser.ms_framework_op_analyser import ( +from mindspore.profiler.analysis.parser.timeline_event.timeline_format_creator import TraceFormatCreator +from mindspore.profiler.analysis.parser.timeline_event.timeline_event_recorder import TimelineEventRecorder +from mindspore.profiler.analysis.parser.timeline_event.msprof_event import MsporfEvent +from mindspore.profiler.analysis.parser.timeline_event.mindspore_op_event import MindSporeOpEvent +from mindspore.profiler.analysis.parser.timeline_event.base_event import BaseEvent +from mindspore.profiler.analysis.parser.timeline_event.timeline_event_recorder import TimelineLayer +from mindspore.profiler.analysis.parser.timeline_analyser.ms_cpu_op_analyser import CpuOpAnalyser +from mindspore.profiler.analysis.parser.timeline_analyser.ms_framework_op_analyser import ( FrameworkOpAnalyser, ) -from mindspore.profiler.analysis.parser.trace_analyser.ascend_msprof_trace_analyser import ( - AscendMsprofTraceAnalyser, +from mindspore.profiler.analysis.parser.timeline_analyser.ascend_msprof_timeline_analyser import ( + AscendMsprofTimelineAnalyser, ) -from mindspore.profiler.analysis.parser.trace_analyser.ms_scope_layer_analyser import ( +from mindspore.profiler.analysis.parser.timeline_analyser.ms_scope_layer_analyser import ( ScopeLayerAnalyser, is_scope_data ) -class AscendTraceAnalyser: +class AscendTimelineAnalyser: """Main timeline analyser that coordinates all sub-analysers.""" def __init__(self): self.cpu_op_analyser = CpuOpAnalyser() self.framework_analyser = FrameworkOpAnalyser() - self.ascend_msprof_analyser = AscendMsprofTraceAnalyser() + self.ascend_msprof_analyser = AscendMsprofTimelineAnalyser() self.scope_analyser = ScopeLayerAnalyser() self.scope_data = [] self.trace_view = [] self.fwk_dispatch_device_kernels = [] + self.trace_event_recoder = TimelineEventRecorder() def analyse(self, data: Dict[str, Any]) -> None: """ @@ -54,13 +59,17 @@ class AscendTraceAnalyser: Returns: List[Dict]: A list of chrome trace events, it can be saved to a trace view json file. """ - self.cpu_op_analyser.analyse(data.get("cpu_op_lines", [])) self.framework_analyser.analyse(data.get("mindspore_op_list", [])) + self.cpu_op_analyser.analyse(data.get("cpu_op_lines", [])) self.ascend_msprof_analyser.analyse(data.get("msprof_timeline", [])) + self.trace_event_recoder.add_process_events(self.framework_analyser.get_process_event()) + self.trace_event_recoder.add_process_events(self.cpu_op_analyser.get_process_event()) + self.trace_event_recoder.add_process_events(self.ascend_msprof_analyser.get_process_event()) + # link the mindspore and device event mindspore_to_npu_flow = self._link_timeline_data( - self.framework_analyser.get_kernel_launch_operations(), + self.trace_event_recoder.get_process_data_by_name(TimelineLayer.MINDSPORE.value).get_cross_pid_flow_event(), self.ascend_msprof_analyser.get_cann_to_npu_flow(), ) @@ -71,9 +80,10 @@ class AscendTraceAnalyser: self.scope_data ) self.scope_analyser.analyse(scope_data) + self.trace_event_recoder.add_process_events(self.scope_analyser.get_process_event()) self.trace_view.extend(mindspore_to_npu_flow) # mindspore to npu flow - self.trace_view.extend(self.framework_analyser.get_flow_events()) # mindspore to mindspore flow + self.trace_view.extend(self.framework_analyser.get_mindspore_to_self_flow()) # mindspore to mindspore flow self.trace_view.extend(self.framework_analyser.get_chrome_trace_events()) # mindspore chrome trace events self.trace_view.extend(self.cpu_op_analyser.get_chrome_trace_events()) # cpu op chrome trace events self.trace_view.extend(self.scope_analyser.get_chrome_trace_events()) # scope chrome trace events @@ -81,15 +91,15 @@ class AscendTraceAnalyser: def _link_timeline_data( self, - fwk_launch_op_list: Dict[int, List[MindSporeOpEvent]], - acl_to_npu_flow_dict: Dict[int, Dict[str, List[CANNEvent]]], + fwk_launch_op_list: Dict[int, List[BaseEvent]], + acl_to_npu_flow_dict: Dict[int, Dict[str, List[BaseEvent]]], ) -> List[Dict]: """ Link MindSpore operations with NPU events. Args: - fwk_launch_op_list (Dict[int, List[MindSporeOpEvent]]): Framework launch operations. - acl_to_npu_flow_dict (Dict[int, Dict[str, List[CANNEvent]]]): ACL to NPU flow dictionary. + fwk_launch_op_list (Dict[str, List[BaseEvent]]): Framework launch operations. + acl_to_npu_flow_dict (Dict[str, Dict[str, List[BaseEvent]]]): ACL to NPU flow dictionary. Returns: List[Dict]: A list of linked MindSpore to NPU flow events. @@ -113,17 +123,17 @@ class AscendTraceAnalyser: for acl_start_time, device_data_list in cann_to_npu_events_sorted: acl_start_time = Decimal(acl_start_time) while index < len(mindspore_launch_op_sorted): - host_op = mindspore_launch_op_sorted[index] - if host_op.ts > acl_start_time: + fwk_launch_op = mindspore_launch_op_sorted[index] + if fwk_launch_op.ts > acl_start_time: break - if acl_start_time <= host_op.te: - for device_data in device_data_list: - device_data.parent = host_op - if not is_scope_data(device_data) and is_scope_data(host_op): - self.scope_data.append(device_data) - self.fwk_dispatch_device_kernels.append(device_data) + if acl_start_time <= fwk_launch_op.te: + for hardware_event in device_data_list: + hardware_event.pre = fwk_launch_op + fwk_launch_op.next.append(hardware_event) + if not is_scope_data(hardware_event) and is_scope_data(fwk_launch_op): + self.scope_data.append(hardware_event) mindspore_to_npu_flow.extend( - TraceEventManager.create_mindspore_to_npu_flow(host_op, device_data) + TraceFormatCreator.create_mindspore_to_npu_flow(fwk_launch_op, hardware_event) ) break index += 1 @@ -133,5 +143,5 @@ class AscendTraceAnalyser: def get_trace_view_data(self) -> List[Dict]: return self.trace_view - def get_fwk_dispatch_device_kernels(self) -> List[BaseEvent]: - return self.fwk_dispatch_device_kernels + def get_trace_event_recoder(self) -> TimelineEventRecorder: + return self.trace_event_recoder diff --git a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/base_trace_analyser.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/base_timeline_analyser.py similarity index 68% rename from mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/base_trace_analyser.py rename to mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/base_timeline_analyser.py index 66a52b6a94dd8523fa791548afbac5de37a3c83b..92857f371fb4826ac937417ea2a7c5a6699499f9 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/base_trace_analyser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/base_timeline_analyser.py @@ -16,12 +16,14 @@ from abc import ABC, abstractmethod from typing import List, Dict, Any +from mindspore.profiler.analysis.parser.timeline_event.timeline_event_recorder import TimelineProcessEvents -class BaseTraceAnalyser(ABC): + +class BaseTimelineAnalyser(ABC): """Base class for all trace analysers.""" def __init__(self): - self.events: List[Dict] = [] + self.process_events: Dict[int, TimelineProcessEvents] = {} @abstractmethod def analyse(self, data: Any) -> None: @@ -30,4 +32,10 @@ class BaseTraceAnalyser(ABC): def get_chrome_trace_events(self) -> List[Dict]: """Return the chrome trace events.""" - return self.events + if not self.process_events: + return [] + for process in self.process_events.values(): + return process.get_all_events_by_timeline_format() + + def get_process_event(self) -> Dict[int, TimelineProcessEvents]: + return self.process_events diff --git a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ms_cpu_op_analyser.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ms_cpu_op_analyser.py similarity index 57% rename from mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ms_cpu_op_analyser.py rename to mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ms_cpu_op_analyser.py index b8af6d85002ac0c5884bb02a60d52639687fa4a0..0f05b85238332fdb7e70af2ef92aa5aa46af7810 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ms_cpu_op_analyser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ms_cpu_op_analyser.py @@ -13,54 +13,50 @@ # limitations under the License. # ============================================================================ -from collections import defaultdict -from typing import List, Dict +from typing import List +from mindspore.profiler.analysis.parser.timeline_event.cpu_op_event import CpuOpEvent +from mindspore.profiler.analysis.parser.timeline_analyser.ms_scope_layer_analyser import is_scope_data +from mindspore.profiler.analysis.parser.timeline_analyser.base_timeline_analyser import BaseTimelineAnalyser +from mindspore.profiler.analysis.parser.timeline_event.timeline_format_creator import TraceFormatCreator +from mindspore.profiler.analysis.parser.timeline_event.timeline_event_recorder import TimelineProcessEvents from mindspore.profiler.parser.ascend_analysis.constant import Constant -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import CpuEvent, BaseEvent -from mindspore.profiler.analysis.parser.trace_analyser.ms_scope_layer_analyser import is_scope_data -from mindspore.profiler.analysis.parser.trace_analyser.base_trace_analyser import BaseTraceAnalyser -from mindspore.profiler.analysis.parser.trace_analyser.trace_event_manager import TraceEventManager -class CpuOpAnalyser(BaseTraceAnalyser): +class CpuOpAnalyser(BaseTimelineAnalyser): """Analyser for CPU OP data.""" def __init__(self): super().__init__() - self.process_thread_map: Dict[int, set] = defaultdict(set) - self.scope_data: List[BaseEvent] = [] + self.scope_data: List[CpuOpEvent] = [] def analyse(self, cpu_info_lines: List[str]) -> None: """Analyse CPU info lines and generate events.""" if not cpu_info_lines: return + process = TimelineProcessEvents(Constant.CPU_OP_PID) + self.process_events[Constant.CPU_OP_PID] = process for line in cpu_info_lines: op_list = line.strip().split(';') op_full_name, op_type, time_info = op_list[0], op_list[1], op_list[-1] for time in time_info.split(" "): start_time, dur, tid = time.split(",") - event = CpuEvent({ + event = CpuOpEvent({ 'name': op_full_name, 'tid': int(tid), 'ts': str(start_time), - 'dur': str(dur), # todo:检查原来代码中这个地方是否有错误 + 'dur': str(dur), 'args': {'type': op_type} }) if is_scope_data(event): self.scope_data.append(event) - self.process_thread_map[int(Constant.CPU_OP)].add(int(tid)) - self.events.append(TraceEventManager.create_x_event(event)) + process.add_event(event) - metadata_events = [ - event - for process_id, thread_ids in self.process_thread_map.items() - for event in TraceEventManager.create_cpu_op_m_event(process_id, thread_ids) - ] + meta_events = TraceFormatCreator.create_cpu_op_m_event(process.get_all_tids()) + for m_event in meta_events: + process.add_metadata(m_event) - self.events.extend(metadata_events) - - def get_scope_data(self) -> List[BaseEvent]: + def get_scope_data(self) -> List[CpuOpEvent]: """Return the scope data.""" return self.scope_data diff --git a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ms_framework_op_analyser.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ms_framework_op_analyser.py similarity index 61% rename from mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ms_framework_op_analyser.py rename to mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ms_framework_op_analyser.py index 79f9da720e0aaa0239f07ee546f295d529513ae5..833213c1d313d970fd5cfe1db443365c1d5be87b 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ms_framework_op_analyser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ms_framework_op_analyser.py @@ -18,12 +18,13 @@ from collections import defaultdict from mindspore import log as logger from mindspore.profiler.parser.ascend_analysis.constant import Constant -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import MindSporeOpEvent -from mindspore.profiler.analysis.parser.trace_analyser.base_trace_analyser import BaseTraceAnalyser -from mindspore.profiler.analysis.parser.trace_analyser.trace_event_manager import TraceEventManager +from mindspore.profiler.analysis.parser.timeline_event.timeline_event_recorder import TimelineProcessEvents +from mindspore.profiler.analysis.parser.timeline_event.mindspore_op_event import MindSporeOpEvent +from mindspore.profiler.analysis.parser.timeline_analyser.base_timeline_analyser import BaseTimelineAnalyser +from mindspore.profiler.analysis.parser.timeline_event.timeline_format_creator import TraceFormatCreator -class FrameworkOpAnalyser(BaseTraceAnalyser): +class FrameworkOpAnalyser(BaseTimelineAnalyser): """Analyser for framework op data.""" _KERNEL_LAUNCH_KEYWORDS = ('KernelLaunch', 'LaunchTask') @@ -31,7 +32,6 @@ class FrameworkOpAnalyser(BaseTraceAnalyser): def __init__(self): super().__init__() self.kernel_launch_ops: Dict[int, List[MindSporeOpEvent]] = defaultdict(list) - self.process_thread_map: Dict[int, set] = defaultdict(set) self.mindspore_flow_events: List[Dict] = [] def analyse(self, mindspore_operations: List[MindSporeOpEvent]) -> None: @@ -39,34 +39,28 @@ class FrameworkOpAnalyser(BaseTraceAnalyser): if not mindspore_operations: logger.warning('No MindSpore framework op found. Skip framework timeline analyse') return + process = TimelineProcessEvents(Constant.MINDSPORE_PID) + self.process_events[Constant.MINDSPORE_PID] = process mindspore_flow_operations = defaultdict(list) - for operation in mindspore_operations: + if operation.ts_raw == 0: # Filter abnormal data + continue if operation.name == Constant.FLOW_OP: - mindspore_flow_operations[operation.id].insert(0, operation) + mindspore_flow_operations[operation.flow_id].insert(0, operation) continue - if operation.id != Constant.INVALID_FLOW_ID: - mindspore_flow_operations[operation.id].append(operation) - - self.process_thread_map[operation.pid].add(operation.tid) + if operation.flow_id != Constant.INVALID_FLOW_ID: + mindspore_flow_operations[operation.flow_id].append(operation) if any(keyword in operation.name for keyword in self._KERNEL_LAUNCH_KEYWORDS): - self.kernel_launch_ops[operation.tid].append(operation) + process.add_cross_pid_flow_event(operation) - if operation.ts_raw == 0: # Filter abnormal data - continue - if operation.dur > 0: - self.events.append(TraceEventManager.create_x_event(operation, "cpu_op")) - else: - self.events.append(TraceEventManager.create_i_event(operation)) + process.add_event(operation) + + meta_events = TraceFormatCreator.create_mindspore_m_event(process.get_all_tids()) + for m_event in meta_events: + process.add_metadata(m_event) - metadata_events = [ - event - for process_id, thread_ids in self.process_thread_map.items() - for event in TraceEventManager.create_mindspore_m_event(process_id, thread_ids) - ] - self.events.extend(metadata_events) self._create_flow_events(mindspore_flow_operations) def _create_flow_events(self, flow_operations: Dict[int, List[MindSporeOpEvent]]) -> None: @@ -77,13 +71,11 @@ class FrameworkOpAnalyser(BaseTraceAnalyser): f'but got {len(flow_pair)} op') logger.warning(msg) continue + flow_pair[1].pre = flow_pair[0] + flow_pair[0].next.append(flow_pair[1]) self.mindspore_flow_events.extend( - TraceEventManager.create_mindspore_to_self_flow(flow_pair[0], flow_pair[1])) - - def get_kernel_launch_operations(self) -> Dict[int, List[MindSporeOpEvent]]: - """Return the kernel launch operations.""" - return self.kernel_launch_ops + TraceFormatCreator.create_mindspore_to_self_flow(flow_pair[0], flow_pair[1])) - def get_flow_events(self) -> List[Dict]: + def get_mindspore_to_self_flow(self) -> List[Dict]: """Return the MindSpore flow events.""" return self.mindspore_flow_events diff --git a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ms_scope_layer_analyser.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ms_scope_layer_analyser.py similarity index 60% rename from mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ms_scope_layer_analyser.py rename to mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ms_scope_layer_analyser.py index e9167b97489598bf954a03ad2b2b6cad3849638e..6a3ffd016d2473c1c914cfba02248feac24f7570 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/ms_scope_layer_analyser.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_analyser/ms_scope_layer_analyser.py @@ -14,33 +14,33 @@ # ============================================================================ from collections import defaultdict -from typing import List, Dict, Tuple, Optional +from typing import List, Dict, Tuple, Optional, Union from decimal import Decimal from mindspore.profiler.parser.ascend_analysis.constant import Constant -from mindspore.profiler.analysis.parser.trace_analyser.base_trace_analyser import BaseTraceAnalyser -from mindspore.profiler.analysis.parser.trace_analyser.trace_event_manager import TraceEventManager -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import ( - BaseEvent, - CpuEvent, - CANNEvent, - ScopeEvent, - MindSporeOpEvent -) - - -class ScopeLayerAnalyser(BaseTraceAnalyser): +from mindspore.profiler.analysis.parser.timeline_analyser.base_timeline_analyser import BaseTimelineAnalyser +from mindspore.profiler.analysis.parser.timeline_event.timeline_format_creator import TraceFormatCreator +from mindspore.profiler.analysis.parser.timeline_event.timeline_event_recorder import TimelineProcessEvents +from mindspore.profiler.analysis.parser.timeline_event.base_event import BaseEvent +from mindspore.profiler.analysis.parser.timeline_event.cpu_op_event import CpuOpEvent +from mindspore.profiler.analysis.parser.timeline_event.scope_layer_event import ScopeLayerEvent +from mindspore.profiler.analysis.parser.timeline_event.mindspore_op_event import MindSporeOpEvent +from mindspore.profiler.analysis.parser.timeline_event.msprof_event import MsporfEvent + + +class ScopeLayerAnalyser(BaseTimelineAnalyser): """Analyser for scope layer data.""" def __init__(self): super().__init__() self.process_thread_map: Dict[str, set] = defaultdict(set) - def analyse(self, event_list: List[BaseEvent]) -> None: + def analyse(self, event_list: List[Union[MindSporeOpEvent, MsporfEvent, CpuOpEvent]]) -> None: """Analyse scope data and generate events.""" if not event_list: return - + process = TimelineProcessEvents(Constant.SCOPE_LAYER_PID) + self.process_events[Constant.SCOPE_LAYER_PID] = process event_list.sort(key=lambda x: x.ts) layers = [] @@ -54,23 +54,28 @@ class ScopeLayerAnalyser(BaseTraceAnalyser): merge = True for layer_depth, layer_name in enumerate(scope_names): if layer_depth >= len(layers): - layers.append(ScopeEvent({"name": layer_name, "tid": layer_depth, - "ts": start_time, "te": end_time})) + layers.append(ScopeLayerEvent({"name": layer_name, "tid": layer_depth, + "ts": start_time, "te": end_time})) continue if merge and layers[layer_depth].name == layer_name: layers[layer_depth].te = end_time else: - self.events.append(TraceEventManager.create_x_event(layers[layer_depth])) - layers[layer_depth] = ScopeEvent( + process.add_event(layers[layer_depth]) + layers[layer_depth] = ScopeLayerEvent( {"name": layer_name, "tid": layer_depth, "ts": start_time, "te": end_time}) merge = False - self.events.extend([TraceEventManager.create_x_event(layer) for layer in layers]) - metadata_events = TraceEventManager.create_scope_m_event(Constant.SCOPE_LAYLER, set(range(len(layers)))) - self.events.extend(metadata_events) + for layer in layers: + process.add_event(layer) + + meta_events = TraceFormatCreator.create_scope_m_event(process.get_all_tids()) + for m_event in meta_events: + process.add_metadata(m_event) @staticmethod - def _parse_scope_data(event: BaseEvent) -> Optional[Tuple[List[str], Decimal, Decimal]]: + def _parse_scope_data( + event: Union[MindSporeOpEvent, MsporfEvent, CpuOpEvent] + ) -> Optional[Tuple[List[str], Decimal, Decimal]]: """ Parse scope data from the given event. @@ -81,10 +86,10 @@ class ScopeLayerAnalyser(BaseTraceAnalyser): Optional[Tuple[List[str], int, int]]: A tuple containing the scope name list, start time, and end time if valid scope data is found; None otherwise. """ - - if event.parent: + # The pre of a hardware op is the mindSpore framework launch op. + if not isinstance(event, CpuOpEvent) and event.pre: event_scope_name = event.name.split('/')[:-1] - parent_scope_name = event.parent.name.split('::')[-1].split('/')[:-1] + parent_scope_name = event.pre.name.split('::')[-1].split('/')[:-1] if event_scope_name and parent_scope_name: scope_name = parent_scope_name if len(parent_scope_name) > len(event_scope_name) else event_scope_name else: @@ -101,7 +106,7 @@ class ScopeLayerAnalyser(BaseTraceAnalyser): def is_scope_data(event: BaseEvent) -> bool: """Determine if the event represents scope data based on specific conditions. """ - if isinstance(event, (CpuEvent, CANNEvent)): + if isinstance(event, (CpuOpEvent, MsporfEvent)): scope_full_name = event.name return scope_full_name and scope_full_name.startswith(Constant.TOP_SCOPE_NAMES) diff --git a/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/__init__.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/base_event.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/base_event.py new file mode 100644 index 0000000000000000000000000000000000000000..60917ce40cb02dbd99f3421e9a2db8f9e433799e --- /dev/null +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/base_event.py @@ -0,0 +1,78 @@ +# Copyright 2023 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. +# ============================================================================ + +from decimal import Decimal +from typing import Dict +from abc import ABC + + +class BaseEvent(ABC): + """Base class for all event types.""" + + def __init__(self, data: Dict): + if not isinstance(data, dict): + raise TypeError("Input data must be dict.") + self._origin_data = data + + @property + def origin_data(self) -> Dict: + return self._origin_data + + @property + def ts(self) -> Decimal: + return Decimal(self._origin_data.get("ts", 0)) + + @property + def te(self) -> Decimal: + return Decimal(self._origin_data.get("te", 0)) + + @property + def pid(self) -> int: + return int(self._origin_data.get("pid", 0)) + + @property + def tid(self) -> int: + return int(self._origin_data.get("tid", 0)) + + @property + def dur(self) -> Decimal: + return Decimal(self._origin_data.get("dur", 0)) + + @property + def name(self) -> str: + return self._origin_data.get("name", "") + + @property + def flow_id(self) -> int: + return int(self._origin_data.get("id")) + + @property + def ph(self) -> str: + return self._origin_data.get("ph") + + @property + def cat(self) -> str: + return self._origin_data.get("cat") + + @property + def args(self) -> str: + return self._origin_data.get("args") + + @property + def unique_id(self) -> str: + return f"{self.pid}-{self.tid}-{self.ts}" + + def to_trace_format(self) -> dict: + return self._origin_data diff --git a/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/cpu_op_event.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/cpu_op_event.py new file mode 100644 index 0000000000000000000000000000000000000000..3302751c9275bbf9c75f6d737b0f9cbd91d324e1 --- /dev/null +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/cpu_op_event.py @@ -0,0 +1,47 @@ +# Copyright 2024 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. +# ============================================================================""" + +from decimal import Decimal + +from mindspore.profiler.parser.ascend_analysis.constant import Constant +from mindspore.profiler.analysis.parser.timeline_event.base_event import BaseEvent +from mindspore.profiler.analysis.parser.timeline_event.timeline_format_creator import TraceFormatCreator + + +class CpuOpEvent(BaseEvent): + """CPU event class.""" + + @property + def ts(self) -> Decimal: + return Decimal(self._origin_data.get("ts", 0)).quantize(Decimal('0.000')) * Decimal('0.001') + + @property + def dur(self) -> Decimal: + return Decimal(self._origin_data.get("dur", 0)).quantize(Decimal('0.000')) * Decimal('1000') + + @property + def te(self) -> Decimal: + return self.ts + self.dur + + @property + def pid(self) -> int: + return int(Constant.CPU_OP_PID) + + @property + def ph(self) -> str: + return Constant.COMPLETE_EVENT + + def to_trace_format(self) -> dict: + return TraceFormatCreator.create_x_event(self) diff --git a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/trace_event.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/mindspore_op_event.py similarity index 61% rename from mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/trace_event.py rename to mindspore/python/mindspore/profiler/analysis/parser/timeline_event/mindspore_op_event.py index a98495adebae03601bba8f8693d75026628f0627..55b24b60980cb7a7a708cb281687ac193f8554f5 100644 --- a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/trace_event.py +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/mindspore_op_event.py @@ -16,127 +16,12 @@ import struct from enum import Enum from decimal import Decimal -from typing import Dict -from abc import ABC +from typing import Dict, Optional, List from mindspore.profiler.parser.ascend_analysis.constant import Constant from mindspore.profiler.analysis.time_convertor import TimeConvertor - - -class BaseEvent(ABC): - """Base class for all event types.""" - - def __init__(self, data: Dict): - if not isinstance(data, dict): - raise TypeError("Input data must be dict.") - self._origin_data = data - - @property - def ts(self) -> Decimal: - return Decimal(self._origin_data.get("ts", 0)) - - @property - def te(self) -> Decimal: - return Decimal(self._origin_data.get("te", 0)) - - @property - def pid(self) -> int: - return int(self._origin_data.get("pid", 0)) - - @property - def tid(self) -> int: - return int(self._origin_data.get("tid", 0)) - - @property - def dur(self) -> Decimal: - return Decimal(self._origin_data.get("dur", 0)) - - @property - def name(self) -> str: - return self._origin_data.get("name", "") - - @property - def id(self) -> int: - return int(self._origin_data.get("id")) - - @property - def cat(self) -> str: - return self._origin_data.get("cat") - - @property - def args(self) -> str: - return self._origin_data.get("args") - - @property - def parent(self) -> None: - return None - - @property - def unique_id(self) -> str: - return f"{self.pid}-{self.tid}-{self.ts}" - - -class CpuEvent(BaseEvent): - """CPU event class.""" - - @property - def ts(self) -> Decimal: - return Decimal(self._origin_data.get("ts", 0)).quantize(Decimal('0.000')) * Decimal('0.001') - - @property - def dur(self) -> Decimal: - return Decimal(self._origin_data.get("dur", 0)).quantize(Decimal('0.000')) * Decimal('1000') - - @property - def pid(self) -> int: - return int(Constant.CPU_OP) - - -class ScopeEvent(BaseEvent): - """Scope event class.""" - - @BaseEvent.te.setter - def te(self, value: Decimal): - self._origin_data["te"] = value - - @property - def dur(self) -> Decimal: - return self.te - self.ts - - @property - def pid(self) -> int: - return int(Constant.SCOPE_LAYLER) - - -class CANNEvent(BaseEvent): - """CANN event class.""" - - def __init__(self, data: Dict): - super().__init__(data) - self.mindspore_op_parent = None - - @property - def te(self) -> Decimal: - return self.ts + self.dur - - @property - def parent(self): - return self.mindspore_op_parent - - @parent.setter - def parent(self, value): - self.mindspore_op_parent = value - - def is_flow_start_event(self) -> bool: - return self._origin_data.get("cat") == Constant.HOST_TO_DEVICE and \ - self._origin_data.get("ph") == Constant.START_FLOW - - def is_flow_end_event(self) -> bool: - return self._origin_data.get("cat") == Constant.HOST_TO_DEVICE and \ - self._origin_data.get("ph") == Constant.END_FLOW - - def is_x_event(self) -> bool: - return self._origin_data.get("ph") == Constant.COMPLETE_EVENT +from mindspore.profiler.analysis.parser.timeline_event.base_event import BaseEvent +from mindspore.profiler.analysis.parser.timeline_event.timeline_format_creator import TraceFormatCreator class MindSporeOpEnum(Enum): @@ -172,6 +57,24 @@ class MindSporeOpEvent(BaseEvent): self._dur_cache = None self._custom_info_cache = "" self._args_cache = None + self._pre: Optional[BaseEvent] = None + self._next: List[BaseEvent] = [] + + @property + def pre(self) -> BaseEvent: + return self._pre + + @pre.setter + def pre(self, event: BaseEvent) -> None: + self._pre = event + + @property + def next(self) -> List[BaseEvent]: + return self._next + + @next.setter + def next(self, event: List[BaseEvent]) -> None: + self._next = event @property def ts_raw(self) -> int: @@ -205,14 +108,14 @@ class MindSporeOpEvent(BaseEvent): @property def pid(self) -> int: - return int(Constant.MINDSPORE) + return int(Constant.MINDSPORE_PID) @property def tid(self) -> int: return int(self.fix_size_data[MindSporeOpEnum.START_THREAD_ID.value]) @property - def id(self) -> int: + def flow_id(self) -> int: return int(self.fix_size_data[MindSporeOpEnum.FLOW_ID.value]) @property @@ -227,6 +130,10 @@ class MindSporeOpEvent(BaseEvent): def level(self) -> int: return int(self.fix_size_data[MindSporeOpEnum.LEVEL.value]) + @property + def ph(self) -> str: + return Constant.COMPLETE_EVENT if self.dur > 0 else Constant.INSTANT_EVENT + @property def custom_info(self) -> str: if not self._custom_info_cache: @@ -256,3 +163,9 @@ class MindSporeOpEvent(BaseEvent): self._custom_info_cache = custom_info.__str__() else: self._args_cache[type_name] = value + + def to_trace_format(self) -> dict: + if self.ph == Constant.COMPLETE_EVENT: + return TraceFormatCreator.create_x_event(self, "cpu_op") + if self.ph == Constant.INSTANT_EVENT: + return TraceFormatCreator.create_i_event(self) diff --git a/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/msprof_event.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/msprof_event.py new file mode 100644 index 0000000000000000000000000000000000000000..1c619f342cb5fbc4fb592665341392b4fec2d47e --- /dev/null +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/msprof_event.py @@ -0,0 +1,69 @@ +# Copyright 2024 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. +# ============================================================================""" + +from decimal import Decimal +from typing import Dict, List, Optional + +from mindspore.profiler.parser.ascend_analysis.constant import Constant +from mindspore.profiler.analysis.parser.timeline_event.base_event import BaseEvent + + +class MsporfEvent(BaseEvent): + """Msprof event class.""" + + def __init__(self, data: Dict): + super().__init__(data) + self._pre: Optional[BaseEvent] = None + self._next: List[BaseEvent] = [] + + @property + def te(self) -> Decimal: + return self.ts + self.dur + + @property + def pre(self) -> BaseEvent: + return self._pre + + @pre.setter + def pre(self, event: BaseEvent) -> None: + self._pre = event + + @property + def next(self) -> List[BaseEvent]: + return self._next + + @next.setter + def next(self, event: BaseEvent) -> None: + self._next.append(event) + + def is_flow_start_event(self) -> bool: + return self._origin_data.get("cat") == Constant.HOST_TO_DEVICE and \ + self._origin_data.get("ph") == Constant.START_FLOW + + def is_flow_end_event(self) -> bool: + return self._origin_data.get("cat") == Constant.HOST_TO_DEVICE and \ + self._origin_data.get("ph") == Constant.END_FLOW + + def is_m_event(self) -> bool: + return self._origin_data.get("ph") == Constant.META_EVENT + + def is_x_event(self) -> bool: + return self._origin_data.get("ph") == Constant.COMPLETE_EVENT + + def is_i_event(self) -> bool: + return self._origin_data.get("ph") == Constant.INSTANT_EVENT + + def is_c_event(self) -> bool: + return self._origin_data.get("ph") == Constant.COUNTER_EVENT diff --git a/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/scope_layer_event.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/scope_layer_event.py new file mode 100644 index 0000000000000000000000000000000000000000..894cde70831d8fb236b3e1ea66aff39d5be893b3 --- /dev/null +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/scope_layer_event.py @@ -0,0 +1,43 @@ +# Copyright 2024 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. +# ============================================================================""" + +from decimal import Decimal + +from mindspore.profiler.parser.ascend_analysis.constant import Constant +from mindspore.profiler.analysis.parser.timeline_event.base_event import BaseEvent +from mindspore.profiler.analysis.parser.timeline_event.timeline_format_creator import TraceFormatCreator + + +class ScopeLayerEvent(BaseEvent): + """Scope event class.""" + + @BaseEvent.te.setter + def te(self, value: Decimal): + self._origin_data["te"] = value + + @property + def dur(self) -> Decimal: + return self.te - self.ts + + @property + def pid(self) -> int: + return int(Constant.SCOPE_LAYER_PID) + + @property + def ph(self) -> str: + return Constant.COMPLETE_EVENT + + def to_trace_format(self) -> dict: + return TraceFormatCreator.create_x_event(self) diff --git a/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/timeline_event_recorder.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/timeline_event_recorder.py new file mode 100644 index 0000000000000000000000000000000000000000..76d9deaf27b4f6a75f06e9330bd48792890bd82b --- /dev/null +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/timeline_event_recorder.py @@ -0,0 +1,171 @@ +# Copyright 2024 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. +# ============================================================================ + +from enum import Enum +from typing import Dict, List, Optional, Set +from collections import defaultdict + +from mindspore.profiler.analysis.parser.timeline_event.base_event import BaseEvent +from mindspore.profiler.parser.ascend_analysis.constant import Constant + + +class TimelineLayer(Enum): + """Timeline layer types.""" + MINDSPORE = "MindSpore" + CPU_OP = "CPU OP" + CANN = "CANN" + SCOPER_LAYER = "Scope Layer" + ASCEND_HARDWARE = "Ascend Hardware" + AI_CORE_FREQ = "AI Core Freq" + HBM = "HBM" + PCLE = "PCle" + HCCS = "HCCS" + LLC = "LLC" + NPU_MEM = "NPU MEM" + STARS_SOC_INFO = "Stars Soc Info" + STARS_Chip_Trans = "Stars Chip Trans" + ACC_PMU = "Acc PMU" + SIO = "SIO" + QOS = "QoS" + OVERLAP_ANALYSIS = "Overlap Analysis" + + +class TimelineProcessEvents: + """ + A class for managing events within a specific process ID (pid) in the timeline. + + This class stores and manages all events associated with a particular process, + including regular events and cross-process flow events. Use add_event() and + add_metadata() to populate the data. Events can be retrieved either by thread ID + or thread name. + """ + def __init__(self, pid): + self.pid = pid + self.name = "" + self.xi_events: Dict[int, List[BaseEvent]] = defaultdict(list) # Store complete and instant events + self.metadata_events: List[Dict] = [] # Store metadata events + self.cross_pid_flow_event: Dict[int, List[BaseEvent]] = defaultdict(list) # Store cross flow events + self.tid_to_name: Dict[int, str] = {} # TID to names mapping (one-to-one) + self.name_to_tid: Dict[str, int] = {} # Name to TID mapping (one-to-one) + + def add_event(self, event: BaseEvent) -> None: + """Add X, I event to timeline.""" + self.xi_events[event.tid].append(event) + + def add_metadata(self, event: Dict) -> None: + """Add metadata event to timeline.""" + if event.get("ph") != Constant.META_EVENT or event.get("pid") != self.pid: + return + + self.metadata_events.append(event) + event_name = event.get("name") + event_args = event.get("args", {}) + + if event_name == Constant.PROCESS_NAME: + self.name = event_args.get("name", "") + elif event_name == Constant.THREAD_NAME: + tid = event.get("tid") + thread_name = event_args.get("name") + if tid is not None and thread_name: + self.tid_to_name[tid] = thread_name + self.name_to_tid[thread_name] = tid + + def get_events_by_tid(self, tid: int) -> List[BaseEvent]: + """Get all events for a specific thread ID.""" + events = self.xi_events.get(tid, []) + return events + + def get_events_by_name(self, name: str) -> List[BaseEvent]: + """Get all events for a specific name.""" + tid = self.name_to_tid.get(name) + return self.xi_events.get(tid, []) + + def add_cross_pid_flow_event(self, event: BaseEvent) -> None: + """Add cross process flow event to timeline.""" + self.cross_pid_flow_event[event.tid].append(event) + + def get_cross_pid_flow_event(self) -> Dict[int, List[BaseEvent]]: + """Get all cross process flow events.""" + return self.cross_pid_flow_event + + def get_all_events(self) -> List[BaseEvent]: + """Get all events in timeline.""" + all_events = [] + for events in self.xi_events.values(): + all_events.extend(events) + return all_events + + def get_all_events_by_timeline_format(self) -> List[Dict]: + """Get all events in timeline format.""" + events = [] + events.extend(self.metadata_events) + events.extend([event.to_trace_format() for event in self.get_all_events()]) + return events + + def get_all_tids(self) -> Set[int]: + """Get all thread IDs in timeline.""" + return set(self.xi_events.keys()) + + def get_names_by_tid(self, tid: int) -> str: + """Get all names associated with a thread ID.""" + return self.tid_to_name.get(tid, "") + + +class TimelineEventRecorder: + """Timeline event recorder.""" + def __init__(self): + """Initialize timeline event recorder.""" + self.processes: Dict[int, TimelineProcessEvents] = {} + self.name_to_pid: Dict[str, int] = {} + self.pid_to_name: Dict[int, str] = {} + + def add_process_events(self, process_dict: Dict[int, TimelineProcessEvents]) -> None: + """Add process events to timeline.""" + for pid, process_data in process_dict.items(): + if process_data.name and process_data.name in self.name_to_pid: + raise ValueError(f"Process name '{process_data.name}' already exists.") + self.processes[pid] = process_data + if process_data.name: + self.name_to_pid[process_data.name] = pid + self.pid_to_name[pid] = process_data.name + + def get_process_data_by_pid(self, pid: int) -> Optional[TimelineProcessEvents]: + """Get process data by process ID.""" + return self.processes.get(pid) + + def get_process_data_by_name(self, name: str) -> Optional[TimelineProcessEvents]: + """Get process data by process name.""" + pid = self.name_to_pid.get(name) + return self.processes.get(pid) if pid is not None else None + + def get_pid_by_name(self, name: str) -> Optional[int]: + """Get process ID by process name.""" + return self.name_to_pid.get(name) + + def get_name_by_pid(self, pid: int) -> Optional[str]: + """Get process name by process ID.""" + return self.pid_to_name.get(pid) + + def get_all_process_names(self) -> List[str]: + """Get all process names.""" + return list(self.name_to_pid.keys()) + + def get_all_pids(self) -> List[int]: + """Get all process IDs.""" + return list(self.processes.keys()) + + def get_all_processes(self) -> List[TimelineProcessEvents]: + """Get all process data.""" + return list(self.processes.values()) diff --git a/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/timeline_format_creator.py b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/timeline_format_creator.py new file mode 100644 index 0000000000000000000000000000000000000000..6af3e5ffbfdf0429bc2f883766837236ff74cd70 --- /dev/null +++ b/mindspore/python/mindspore/profiler/analysis/parser/timeline_event/timeline_format_creator.py @@ -0,0 +1,199 @@ +# Copyright 2023 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. +# ============================================================================ + +from typing import Dict, List, Set + +from mindspore.profiler.parser.ascend_analysis.constant import Constant +from mindspore.profiler.analysis.parser.timeline_event.base_event import BaseEvent + + +class TraceFormatCreator: + """Chrome trace format json object creator.""" + + @classmethod + def create_x_event(cls, event: BaseEvent, cat: str = '') -> Dict: + """Create a complete (X) event.""" + return { + "ph": Constant.COMPLETE_EVENT, + "name": event.name, + "pid": event.pid, + "tid": event.tid, + "ts": str(event.ts), + "dur": str(event.dur), + "cat": cat, + "args": event.args + } + + @classmethod + def create_i_event(cls, event: BaseEvent) -> Dict: + """Create an instant (i) event.""" + return { + "name": event.name, + "ph": Constant.INSTANT_EVENT, + "ts": str(event.ts), + "pid": event.pid, + "tid": event.tid, + "args": event.args + } + + @classmethod + def _create_thread_metadata(cls, pid: int, tid: int) -> List[Dict]: + """Create thread metadata events.""" + return [ + { + "ph": Constant.META_EVENT, + "name": Constant.THREAD_NAME, + "pid": pid, + "tid": tid, + "args": {"name": f"Thread {tid}"} + }, + { + "ph": Constant.META_EVENT, + "name": Constant.THREAD_SORT, + "pid": pid, + "tid": tid, + "args": {"sort_index": tid} + } + ] + + @classmethod + def create_mindspore_m_event(cls, tid_list: Set[int]) -> List[Dict]: + """Create metadata events for MindSpore.""" + events = [ + { + "ph": Constant.META_EVENT, + "name": Constant.PROCESS_NAME, + "pid": Constant.MINDSPORE_PID, + "tid": 0, + "args": {"name": "MindSpore"} + }, + { + "ph": Constant.META_EVENT, + "name": Constant.PROCESS_SORT, + "pid": Constant.MINDSPORE_PID, + "tid": 0, + "args": {"sort_index": Constant.MINDSPORE_SORT_IDX} + }, + { + "ph": Constant.META_EVENT, + "name": Constant.PROCESS_LABEL, + "pid": Constant.MINDSPORE_PID, + "tid": 0, + "args": {"labels": "CPU"} + } + ] + + for tid in tid_list: + events.extend(cls._create_thread_metadata(Constant.MINDSPORE_PID, tid)) + return events + + @classmethod + def create_cpu_op_m_event(cls, tid_list: Set[int]) -> List[Dict]: + """Create metadata events for CPU operations.""" + events = [ + { + "ph": Constant.META_EVENT, + "name": Constant.PROCESS_NAME, + "pid": Constant.CPU_OP_PID, + "tid": 0, + "args": {"name": "CPU OP"} + }, + { + "ph": Constant.META_EVENT, + "name": Constant.PROCESS_SORT, + "pid": Constant.CPU_OP_PID, + "tid": 0, + "args": {"sort_index": Constant.CPU_OP_SORT_IDX} + } + ] + + for tid in tid_list: + events.extend(cls._create_thread_metadata(Constant.CPU_OP_PID, tid)) + return events + + @classmethod + def create_scope_m_event(cls, tid_list: Set[int]) -> List[Dict]: + """Create metadata events for scope.""" + events = [ + { + "ph": Constant.META_EVENT, + "name": Constant.PROCESS_NAME, + "pid": Constant.SCOPE_LAYER_PID, + "tid": 0, + "args": {"name": "Scope Layer"} + }, + { + "ph": Constant.META_EVENT, + "name": Constant.PROCESS_SORT, + "pid": Constant.SCOPE_LAYER_PID, + "tid": 0, + "args": {"sort_index": Constant.SCOPE_LAYER_SORT_IDX} + } + ] + + for tid in tid_list: + events.extend(cls._create_thread_metadata(Constant.SCOPE_LAYER_PID, tid)) + return events + + @classmethod + def _create_flow_event_pair(cls, name: str, start_event: BaseEvent, + end_event: BaseEvent, cat: str, flow_id: str = None) -> List[Dict]: + """Create a pair of flow events.""" + if flow_id is None: + flow_id = str(end_event.ts) + + return [ + { + "ph": Constant.START_FLOW, + "bp": "e", + "name": name, + "id": flow_id, + "pid": start_event.pid, + "tid": start_event.tid, + "ts": str(start_event.ts), + "cat": cat + }, + { + "ph": Constant.END_FLOW, + "bp": "e", + "name": name, + "id": flow_id, + "pid": end_event.pid, + "tid": end_event.tid, + "ts": str(end_event.ts), + "cat": cat + } + ] + + @classmethod + def create_mindspore_to_npu_flow(cls, start_event: BaseEvent, end_event: BaseEvent) -> List[Dict]: + """Create flow events linking MindSpore operator and NPU kernel.""" + return cls._create_flow_event_pair( + "mindspore_to_npu", + start_event, + end_event, + Constant.MINDSPORE_NPU_FLOW_CAT + ) + + @classmethod + def create_mindspore_to_self_flow(cls, start_event: BaseEvent, end_event: BaseEvent) -> List[Dict]: + """Create flow events linking MindSpore operator to itself.""" + return cls._create_flow_event_pair( + "mindspore_to_self", + start_event, + end_event, + Constant.MINDSPORE_SELF_FLOW_CAT, + str(start_event.flow_id) + ) diff --git a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/trace_event_manager.py b/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/trace_event_manager.py deleted file mode 100644 index bb8651e3b1340b02e1530ad4949daacc477daa6a..0000000000000000000000000000000000000000 --- a/mindspore/python/mindspore/profiler/analysis/parser/trace_analyser/trace_event_manager.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright 2023 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. -# ============================================================================ - -from typing import Dict, List - -from mindspore.profiler.parser.ascend_analysis.constant import Constant -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import BaseEvent - - -class TraceEventManager: - """Chrome trace format json object manager.""" - - @classmethod - def create_x_event(cls, event: BaseEvent, cat: str = '') -> Dict: - """ - Create an X event. - - Args: - event (BaseEvent): The event to create. - cat (str, optional): The category of the event. Defaults to ''. - - Returns: - Dict: The created X event. - """ - return { - "ph": "X", - "name": event.name, - "pid": event.pid, - "tid": event.tid, - "ts": str(event.ts), - "dur": str(event.dur), - "cat": cat, - "args": event.args - } - - @classmethod - def create_i_event(cls, event: BaseEvent) -> Dict: - """ - Create an i event. - - Args: - event (BaseEvent): The event to create. - - Returns: - Dict: The created i event. - """ - return { - "name": event.name, - "ph": "i", - "ts": str(event.ts), - "pid": event.pid, - "tid": event.tid, - "args": event.args - } - - @classmethod - def create_mindspore_m_event(cls, pid: int, tid_list: set) -> List: - """ - Create some metadata events for MindSpore. - - Args: - pid (int): The process ID. - tid_list (set): The set of thread IDs. - - Returns: - List: A list of metadata events. - """ - event_list = [ - {"ph": "M", "name": Constant.PROCESS_NAME, "pid": pid, "tid": 0, "args": {"name": "MindSpore"}}, - {"ph": "M", "name": Constant.PROCESS_LABEL, "pid": pid, "tid": 0, "args": {"labels": "CPU"}}, - {"ph": "M", "name": Constant.PROCESS_SORT, "pid": pid, - "tid": 0, "args": {"sort_index": Constant.MINDSPORE}}, - ] - for tid in tid_list: - event_list.extend([ - {"ph": "M", "name": Constant.THREAD_NAME, "pid": pid, "tid": tid, "args": {"name": f"Thread {tid}"}}, - {"ph": "M", "name": Constant.THREAD_SORT, "pid": pid, "tid": tid, "args": {"sort_index": tid}} - ]) - return event_list - - @classmethod - def create_cpu_op_m_event(cls, pid: int, tid_list: set) -> List: - """ - Create some metadata events for CPU operations. - - Args: - pid (int): The process ID. - tid_list (set): The set of thread IDs. - - Returns: - List: A list of metadata events. - """ - event_list = [ - {"ph": "M", "name": Constant.PROCESS_NAME, "pid": pid, "tid": 0, "args": {"name": "CPU OP"}}, - {"ph": "M", "name": Constant.PROCESS_SORT, "pid": pid, "tid": 0, "args": {"sort_index": Constant.CPU_OP}}, - ] - for tid in tid_list: - event_list.extend([ - {"ph": "M", "name": Constant.THREAD_NAME, "pid": pid, "tid": tid, "args": {"name": f"Thread {tid}"}}, - {"ph": "M", "name": Constant.THREAD_SORT, "pid": pid, "tid": tid, "args": {"sort_index": tid}} - ]) - return event_list - - @classmethod - def create_scope_m_event(cls, pid: int, tid_list: set) -> List: - """ - Create some metadata events for scope. - - Args: - pid (int): The process ID. - tid_list (set): The set of thread IDs. - - Returns: - List: A list of metadata events. - """ - event_list = [ - {"ph": "M", "name": Constant.PROCESS_NAME, "pid": pid, "tid": 0, "args": {"name": "Scope Layer"}}, - {"ph": "M", "name": Constant.PROCESS_SORT, "pid": pid, - "tid": 0, "args": {"sort_index": Constant.SCOPE_LAYLER}}, - ] - for tid in tid_list: - event_list.extend([ - {"ph": "M", "name": Constant.THREAD_NAME, "pid": pid, "tid": tid, "args": {"name": f"Thread {tid}"}}, - {"ph": "M", "name": Constant.THREAD_SORT, "pid": pid, "tid": tid, "args": {"sort_index": tid}} - ]) - return event_list - - @classmethod - def create_mindspore_to_npu_flow(cls, start_event: BaseEvent, end_event: BaseEvent) -> List: - """ - Create flow events linking MindSpore operator and NPU kernel. - - Args: - start_event (BaseEvent): The starting event. - end_event (BaseEvent): The ending event. - - Returns: - List: A list of flow events. - """ - flow_id = str(end_event.ts) - return [ - { - "ph": "s", "bp": "e", "name": "mindspore_to_npu", "id": flow_id, - "pid": start_event.pid, "tid": start_event.tid, "ts": str(start_event.ts), - "cat": "async_npu" - }, - { - "ph": "f", "bp": "e", "name": "mindspore_to_npu", "id": flow_id, - "pid": end_event.pid, "tid": end_event.tid, "ts": str(end_event.ts), - "cat": "async_npu" - } - ] - - @classmethod - def create_mindspore_to_self_flow(cls, start_event: BaseEvent, end_event: BaseEvent) -> List: - """ - Create flow events linking MindSpore operator to itself. - - Args: - start_event (BaseEvent): The starting event. - end_event (BaseEvent): The ending event. - - Returns: - List: A list of flow events. - """ - flow_id = start_event.id - return [ - { - "ph": "s", "bp": "e", "name": "mindspore_to_self", "id": flow_id, - "pid": start_event.pid, "tid": start_event.tid, "ts": str(start_event.ts), - "cat": "async_mindspore" - }, - { - "ph": "f", "bp": "e", "name": "mindspore_to_self", "id": flow_id, - "pid": end_event.pid, "tid": end_event.tid, "ts": str(end_event.ts), - "cat": "async_mindspore" - } - ] diff --git a/mindspore/python/mindspore/profiler/analysis/viewer/ms_dataset_viewer.py b/mindspore/python/mindspore/profiler/analysis/viewer/ms_dataset_viewer.py index af659852201fc8e5e0301373e030938becc2e927..2c25b7b13cafea5f9725133e51d323e0ae18f06d 100644 --- a/mindspore/python/mindspore/profiler/analysis/viewer/ms_dataset_viewer.py +++ b/mindspore/python/mindspore/profiler/analysis/viewer/ms_dataset_viewer.py @@ -19,7 +19,7 @@ from typing import List, Dict, Any from mindspore.profiler.analysis.viewer.base_viewer import BaseViewer from mindspore.profiler.common.file_manager import FileManager -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import MindSporeOpEvent +from mindspore.profiler.analysis.parser.timeline_event.mindspore_op_event import MindSporeOpEvent class MsDatasetViewer(BaseViewer): diff --git a/mindspore/python/mindspore/profiler/analysis/viewer/ms_minddata_viewer.py b/mindspore/python/mindspore/profiler/analysis/viewer/ms_minddata_viewer.py index 6f8471fc950ba0e3e98401e9fc287445cf4706e5..3fe21ce88782ce0092064ab696daf8ff449b159b 100644 --- a/mindspore/python/mindspore/profiler/analysis/viewer/ms_minddata_viewer.py +++ b/mindspore/python/mindspore/profiler/analysis/viewer/ms_minddata_viewer.py @@ -203,11 +203,10 @@ class MindDataPiplineSummaryViewer(BaseViewer): return summary_dict def _save_data(self, summary_dict: Dict[str, Any]) -> None: - FileManager.create_json_file(self._save_paths['json'], summary_dict, "") + FileManager.create_json_file(self._save_paths['json_file'], summary_dict) FileManager.create_csv_file( self._save_paths['csv_file'], - list(summary_dict.values()), - list(summary_dict.keys()) + [[key] + value for key, value in summary_dict.items()] ) def _check_and_update_summary(self, summary_dict: Dict[str, Any]) -> None: @@ -241,7 +240,7 @@ class MindDataPiplineSummaryViewer(BaseViewer): """ # Since there may be non-linear pipelines, the processed op info needs to be sorted before final output is # produced and saved. - op_id_info_list = sorted(pipeline_info[0], key=lambda item: item[0]) + op_id_info_list = sorted(pipeline_info[0].items(), key=lambda item: item[0]) return_dict = defaultdict(list) dict_opid_parent_id = {} @@ -514,7 +513,7 @@ class BottleneckAnalyzer: bottleneck = self.pipeline_ops[op_id] suggestion = self._format_high_cpu_usage_suggestion(op_id, wkr_cpu) - elif wkr_cpu < self._THRESHOLDS['AVG_CPU_UTIL_PCT_PER_WORKER_MINIMUM']: + elif wkr_cpu < self._THRESHOLDS['_AVG_CPU_UTIL_PCT_PER_WORKER_MINIMUM']: in_q_usage = self.queue_utilization_pct[in_op_id] if in_op_id != self.op_id_not_exist and (in_q_usage < self._THRESHOLDS['_IN_QUEUE_UTIL_PCT_MAXIMUM'] or out_q - in_q_usage > self._THRESHOLDS['_IN_OUT_QUEUE_UTIL_PCT_DIFF_MAXIMUM']): diff --git a/mindspore/python/mindspore/profiler/common/file_manager.py b/mindspore/python/mindspore/profiler/common/file_manager.py index 21ad7bea6ec8a9193f81db7577127ce76a844f39..2c0714fa2705e5e7feb194a0e4c59f9bb6979024 100644 --- a/mindspore/python/mindspore/profiler/common/file_manager.py +++ b/mindspore/python/mindspore/profiler/common/file_manager.py @@ -139,7 +139,7 @@ class FileManager: try: with open(file_path, "r") as file: for line in file.readlines(): - result_data.append(line.strip().split(",")) + result_data.append(line.strip()) except Exception as err: raise RuntimeError(f"Failed to read the file: {file_path}") from err return result_data diff --git a/mindspore/python/mindspore/profiler/common/tlv_decoder.py b/mindspore/python/mindspore/profiler/common/tlv_decoder.py index cd3a8f34724e75d2ff2c51cb0faf21e7b3e94db3..5f29cb0dddb4257c58f9c3d243f9bb41b0b958d3 100644 --- a/mindspore/python/mindspore/profiler/common/tlv_decoder.py +++ b/mindspore/python/mindspore/profiler/common/tlv_decoder.py @@ -20,7 +20,6 @@ from typing import List, Dict, Union from mindspore import log as logger from mindspore.profiler.parser.ascend_analysis.constant import Constant -from mindspore.profiler.analysis.parser.trace_analyser.trace_event import MindSporeOpEvent class TLVDecoder: @@ -29,17 +28,17 @@ class TLVDecoder: Args: all_bytes(bytes): all the bytes data of tlv format binary file. - event_class(MindSporeOpEvent): class of events to decode. + event_class(any): class of events to decode. fix_data_struct_size(int): a constant value. Return: - List[MindSporeOpEvent]: event list of input event class. + List[any]: event list of input event class. """ _type_len = 2 _length_len = 4 @classmethod - def decode(cls, all_bytes: bytes, event_class: MindSporeOpEvent, fix_data_struct_size: int): + def decode(cls, all_bytes: bytes, event_class: any, fix_data_struct_size: int): """Decode all the data.""" result_data = [] records = cls.tlv_list_decode(all_bytes) diff --git a/mindspore/python/mindspore/profiler/parser/ascend_analysis/constant.py b/mindspore/python/mindspore/profiler/parser/ascend_analysis/constant.py index 2e62204522f5e2724355585ce26ab29c3abf41df..05ac26ca985048bdc72797fc02fb534b1758b1ac 100644 --- a/mindspore/python/mindspore/profiler/parser/ascend_analysis/constant.py +++ b/mindspore/python/mindspore/profiler/parser/ascend_analysis/constant.py @@ -24,7 +24,11 @@ class Constant: END_FLOW = "f" META_EVENT = 'M' COMPLETE_EVENT = 'X' + INSTANT_EVENT = 'i' + COUNTER_EVENT = 'C' FLOW_OP = "flow" + MINDSPORE_NPU_FLOW_CAT = "async_npu" + MINDSPORE_SELF_FLOW_CAT = "async_mindspore" INVALID_FLOW_ID = 18446744073709551615 DEFAULT_PROCESS_NUMBER = os.cpu_count() // 2 @@ -60,6 +64,15 @@ class Constant: PROFILER_DIR = "profiler" TOP_SCOPE_NAMES = ('Default', 'Gradients', 'recompute_Default') + # the index of timeline pid + MINDSPORE_PID = 1 + CPU_OP_PID = 2 + SCOPE_LAYER_PID = 3 + # the index of timeline sort idx + MINDSPORE_SORT_IDX = 1 + CPU_OP_SORT_IDX = 2 + SCOPE_LAYER_SORT_IDX = 12 + # the index of modules of timeline MINDSPORE = 1 CPU_OP = 2 diff --git a/mindspore/python/mindspore/profiler/parser/ascend_analysis/function_event.py b/mindspore/python/mindspore/profiler/parser/ascend_analysis/function_event.py index 7bdde17b8f2df5ac636e33b8b678d33cef514e8c..c00efd7423c89d03bfb4cf27ed0ba1d587c045b8 100644 --- a/mindspore/python/mindspore/profiler/parser/ascend_analysis/function_event.py +++ b/mindspore/python/mindspore/profiler/parser/ascend_analysis/function_event.py @@ -168,8 +168,8 @@ class MindSporeOpEvent(BaseEvent): def _get_args(self, fix_size_data) -> Dict: """Get the rest information saved in args""" args = { - Constant.SEQUENCE_UNMBER: int(fix_size_data[MindSporeOpEnum.SEQUENCE_UNMBER.value]), - Constant.FORWORD_THREAD_ID: int(fix_size_data[MindSporeOpEnum.FORWORD_THREAD_ID.value])} + Constant.SEQUENCE_NUMBER: int(fix_size_data[MindSporeOpEnum.SEQUENCE_UNMBER.value]), + Constant.FORWARD_THREAD_ID: int(fix_size_data[MindSporeOpEnum.FORWORD_THREAD_ID.value])} for type_name, type_id in self._tlv_type_dict.items(): if type_name == Constant.OP_NAME or type_id not in self._orig_data.keys(): continue diff --git a/mindspore/python/mindspore/profiler/platform/npu_profiler.py b/mindspore/python/mindspore/profiler/platform/npu_profiler.py index 56cc9bb2869c332d4241e322d927d271ffbabca8..5a64c147be698240e8d350fed130caa51445cef5 100644 --- a/mindspore/python/mindspore/profiler/platform/npu_profiler.py +++ b/mindspore/python/mindspore/profiler/platform/npu_profiler.py @@ -36,6 +36,11 @@ from mindspore.profiler.analysis.viewer.ms_dataset_viewer import MsDatasetViewer from mindspore.profiler.analysis.time_convertor import TimeConvertor from mindspore.profiler.analysis.parser.framework_cann_relation_parser import FrameworkCannRelationParser from mindspore.profiler.analysis.viewer.ascend_timeline_viewer import AscendTimelineViewer +from mindspore.profiler.analysis.parser.ms_minddata_parser import MindDataParser +from mindspore.profiler.analysis.viewer.ms_minddata_viewer import ( + MindDataPipelineRawViewer, + MindDataPiplineSummaryViewer +) @PROFILERS.register_module(DeviceTarget.ASCEND.value) class NpuProfiler(BaseProfiler): @@ -158,5 +163,12 @@ class NPUProfilerAnalysis: flow_name="timeline_flow", show_process=True, ) + cls._task_mgr.create_flow( + MindDataParser(**kwargs) + .register_post_hook(MindDataPipelineRawViewer(**kwargs).save) + .register_post_hook(MindDataPiplineSummaryViewer(**kwargs).save), + flow_name="minddata_flow", + show_process=True, + ) cls._task_mgr.run() cls._task_mgr.print_cost_time_table()