From dd183bec9e0727fac9134118891fe560308b73ca Mon Sep 17 00:00:00 2001 From: huxianglong Date: Mon, 23 Dec 2024 18:08:07 +0800 Subject: [PATCH 1/5] first clean --- .../cluster_advice/cluster_advice_base.py | 6 +- .../cluster_advice/cluster_pipeline_advice.py | 72 +++++++++---------- .../common_func_advisor/trace_view_json.py | 17 +++-- .../compute_advice/npu_fused/csv_analyzer.py | 37 +++++----- .../compute_advice/npu_fused/op_perf.py | 24 ++++--- profiler/advisor/advisor_backend/logger.py | 2 +- .../timeline_advice/op_schedule_advice.py | 8 ++- .../timeline_advice/timeline_advice_base.py | 17 ++--- .../advisor/analyzer/analyzer_controller.py | 28 ++++---- profiler/advisor/analyzer/base_analyzer.py | 12 ++-- .../ai_core_freq/ai_core_freq_checker.py | 4 +- .../computation/aicpu/aicpu_checker.py | 3 +- .../analyzer/computation/operator_checker.py | 34 ++++----- .../computation/profiling_analyzer.py | 2 +- .../analyzer/memory/memory_analyzer.py | 7 +- .../fusion_ops/fusion_ops_analyzer.py | 21 +++--- profiler/advisor/common/analyzer_scopes.py | 3 +- .../advisor/common/async_analysis_status.py | 3 +- profiler/advisor/common/enum_params_parser.py | 6 +- profiler/advisor/common/graph/graph.py | 8 +-- profiler/advisor/common/graph/graph_match.py | 6 +- profiler/advisor/common/graph/graph_parser.py | 13 ++-- profiler/advisor/common/profiling/ge_info.py | 4 +- profiler/advisor/common/profiling/msprof.py | 4 +- .../advisor/common/profiling/op_summary.py | 4 +- profiler/advisor/common/profiling/tasktime.py | 5 +- profiler/advisor/common/version_control.py | 4 +- profiler/advisor/config/config.py | 11 ++- .../advisor/dataset/profiling/device_info.py | 43 +++++++---- .../dataset/profiling/profiling_dataset.py | 19 ++--- .../dataset/profiling/profiling_parser.py | 44 ++++++------ .../advisor/dataset/timeline_event_dataset.py | 5 +- .../timeline_op_collector.py | 15 ++++ .../advisor/display/prompt/base_prompt.py | 1 + .../display/prompt/en/dynamic_shape_prompt.py | 8 +-- profiler/advisor/utils/log.py | 15 ++++ profiler/advisor/utils/tools.py | 27 +++++-- profiler/advisor/utils/utils.py | 3 +- 38 files changed, 305 insertions(+), 240 deletions(-) diff --git a/profiler/advisor/advisor_backend/cluster_advice/cluster_advice_base.py b/profiler/advisor/advisor_backend/cluster_advice/cluster_advice_base.py index 60e5a7548..a9dbf4c05 100644 --- a/profiler/advisor/advisor_backend/cluster_advice/cluster_advice_base.py +++ b/profiler/advisor/advisor_backend/cluster_advice/cluster_advice_base.py @@ -14,15 +14,17 @@ # limitations under the License. import os - from abc import abstractmethod + from advice_base import AdviceBase from cluster_analysis import Interface + from profiler.advisor.advisor_backend.logger import Logger from profiler.prof_common.constant import Constant logger = Logger() + class ClusterAdviceBase(AdviceBase): def __init__(self, collection_path: str): super().__init__(collection_path) @@ -67,4 +69,4 @@ class ClusterAdviceBase(AdviceBase): def output(self): """ output relevant data - """ \ No newline at end of file + """ diff --git a/profiler/advisor/advisor_backend/cluster_advice/cluster_pipeline_advice.py b/profiler/advisor/advisor_backend/cluster_advice/cluster_pipeline_advice.py index 47fd73d0a..38e2e766b 100644 --- a/profiler/advisor/advisor_backend/cluster_advice/cluster_pipeline_advice.py +++ b/profiler/advisor/advisor_backend/cluster_advice/cluster_pipeline_advice.py @@ -13,31 +13,31 @@ # See the License for the specific language governing permissions and # limitations under the License. +import multiprocessing import os import time -import multiprocessing -from typing import Dict -from typing import Optional -from typing import Deque -from typing import List -from typing import Tuple from collections import defaultdict from collections import deque -from decimal import Decimal from dataclasses import dataclass +from decimal import Decimal +from typing import Deque +from typing import Dict +from typing import List +from typing import Optional +from typing import Tuple +from cluster_advice.cluster_advice_base import ClusterAdviceBase +from cluster_data_preprocess.pytorch_data_preprocessor import PytorchDataPreprocessor from common_func_advisor.constant import Constant from common_func_advisor.trace_view_preprocessor import FineTraceViewData from common_func_advisor.trace_view_preprocessor import TraceViewPreProcessor -from cluster_advice.cluster_advice_base import ClusterAdviceBase -from cluster_data_preprocess.pytorch_data_preprocessor import PytorchDataPreprocessor + from profiler.advisor.advisor_backend.logger import Logger from profiler.prof_common.file_manager import FileManager logger = Logger() - @dataclass class PipelineTimeSlice: start: str = "" @@ -124,6 +124,7 @@ class PipelineTraceViewer: return data + class ClusterPipelineAdvice(ClusterAdviceBase): BUBBLE = "Bubble" STAGE = "Stage" @@ -139,22 +140,6 @@ class ClusterPipelineAdvice(ClusterAdviceBase): self.cur_bottleneck = {} self.cur_advices = "" - - @staticmethod - def _align_trace_bound(results: List) -> None: - """ - align all rank trace bound for better visualization - """ - start_list, end_list = [], [] - for res in results: - start_list.append(res[0].start) - end_list.append(res[-1].end) - - # update all rank trace bound - for res in results: - res[0].start = min(start_list) - res[-1].end = max(end_list) - @staticmethod def load_trace_view_data(json_path) -> Optional[FineTraceViewData]: """ @@ -272,6 +257,21 @@ class ClusterPipelineAdvice(ClusterAdviceBase): return res + @staticmethod + def _align_trace_bound(results: List) -> None: + """ + align all rank trace bound for better visualization + """ + start_list, end_list = [], [] + for res in results: + start_list.append(res[0].start) + end_list.append(res[-1].end) + + # update all rank trace bound + for res in results: + res[0].start = min(start_list) + res[-1].end = max(end_list) + def run(self) -> dict: """ Unified entrance interface @@ -291,8 +291,8 @@ class ClusterPipelineAdvice(ClusterAdviceBase): process all rank profiling data by using multi-process """ start_time = time.time() - logger.info("Start to process %s rank profiling data with %s workers." - ,str(len(self.rank_prof_dirs)),str(self.worker_num)) + logger.info("Start to process %s rank profiling data with %s workers.", + str(len(self.rank_prof_dirs)), str(self.worker_num)) with multiprocessing.Pool(self.worker_num) as pool: results = pool.map(self.work, self.rank_prof_dirs.items()) @@ -302,7 +302,7 @@ class ClusterPipelineAdvice(ClusterAdviceBase): else: self.cur_data += PipelineTraceViewer().gen_stage_bubble_trace_data(rank_id, res) time_cost = time.time() - start_time - logger.info("Pipline view data process finished, cost %2f s.",time_cost) + logger.info("Pipline view data process finished, cost %2f s.", time_cost) def work(self, kv: Tuple[int, str]) -> Tuple[List[PipelineTimeSlice], bool]: """ @@ -310,29 +310,29 @@ class ClusterPipelineAdvice(ClusterAdviceBase): """ show_fp_bp = False rank_id, rank_prof_dir = kv - logger.info("[Rank %s] Start to process rank profiling data.",rank_id) + logger.info("[Rank %s] Start to process rank profiling data.", rank_id) json_path = os.path.join(rank_prof_dir, Constant.ASCEND_PROFILER_OUTPUT, Constant.TRACE_VIEW_JSON) fine_data = self.load_trace_view_data(json_path) if not fine_data.hcom_ops or not fine_data.hcom_tids: logger.error("[Rank %s] No hcom send recv ops found, make sure the trace view data is " - "pipeline parallel sense.",str(rank_id)) + "pipeline parallel sense.", str(rank_id)) return [], show_fp_bp timeslice_list = self.get_pipeline_timeslice(fine_data.hcom_ops, fine_data.hcom_tids, fine_data.min_ts, fine_data.max_ts) if not fine_data.fp_ops or not fine_data.bp_ops: logger.info("[Rank %s] No frameWork data in trace view, only show stage and bubble." - ,str(rank_id)) + , str(rank_id)) elif len(fine_data.hcom_tids) > 1: logger.warning("[Rank %s] More than one hcom tid found, only show stage and bubble." - ,str(rank_id)) + , str(rank_id)) else: logger.info("[Rank %s] Found frameWork data in trace view, show fp bp and bubble." - ,rank_id) + , rank_id) bp_ops = self.get_fp_bp_bound_ops(fine_data) self.update_stage_fp_bp(timeslice_list, bp_ops) show_fp_bp = True - logger.info("[Rank %s] Rank profiling data process finished.",str(rank_id)) + logger.info("[Rank %s] Rank profiling data process finished.", str(rank_id)) return timeslice_list, show_fp_bp @@ -373,7 +373,7 @@ class ClusterPipelineAdvice(ClusterAdviceBase): if rank_id in data_map: rank_prof_dirs[rank_id] = data_map[rank_id] else: - logger.warning('Rank %s not found in %s',str(self.collection_path)) + logger.warning('Rank %s not found in %s', str(self.collection_path)) return rank_prof_dirs diff --git a/profiler/advisor/advisor_backend/common_func_advisor/trace_view_json.py b/profiler/advisor/advisor_backend/common_func_advisor/trace_view_json.py index a6337cd57..26a46ed88 100644 --- a/profiler/advisor/advisor_backend/common_func_advisor/trace_view_json.py +++ b/profiler/advisor/advisor_backend/common_func_advisor/trace_view_json.py @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os - from abc import abstractmethod from dataclasses import dataclass from dataclasses import field @@ -23,11 +21,12 @@ from typing import List import pandas as pd -from profiler.prof_common.file_manager import FileManager from profiler.advisor.advisor_backend.logger import Logger +from profiler.prof_common.file_manager import FileManager logger = Logger() + @dataclass class TraceObj: ph: str = "" @@ -97,21 +96,21 @@ class TraceViewJson: self.torch_2_npu_flow_events: Dict[str, FlowEvent] = dict() traces = FileManager.read_json_file(path) self._load_obj(traces) - + def get_call_stack(self, data: pd.DataFrame, index_id: int, ts_col: str) -> str: if ts_col not in data.columns.tolist(): - logger.error("No %s col found in data columns.",str(ts_col)) + logger.error("No %s col found in data columns.", str(ts_col)) return "" row = data.loc[index_id] timestamp = row[ts_col] flow_event = self.get_torch_2_npu_flow_event(timestamp) if not flow_event.valid(): - logger.error("Get flow event failed for pattern %s.",str(row['pattern'])) + logger.error("Get flow event failed for pattern %s.", str(row['pattern'])) return "" flow_event_s_key = flow_event.s_point_ts python_dur_events = self.get_python_dur_events_contain_ts(flow_event_s_key) if not python_dur_events: - logger.error("No python dur event found for pattern %s.",str(row['pattern'])) + logger.error("No python dur event found for pattern %s.", str(row['pattern'])) return "" # 保持新老版本callstack兼容性 if python_dur_events[0].args.get("Call stack"): @@ -126,7 +125,7 @@ class TraceViewJson: def get_torch_2_npu_flow_event(self, end_time) -> FlowEvent: if not self.torch_2_npu_flow_events or not self.torch_2_npu_flow_events.get(end_time): - logger.error("Find flow event failed for ts: %s",str(end_time)) + logger.error("Find flow event failed for ts: %s", str(end_time)) return FlowEvent() return self.torch_2_npu_flow_events.get(end_time) @@ -151,7 +150,7 @@ class TraceViewJson: for check_process in check_processes: if check_process in self.processes: continue - logger.error("%s process not found in json.",str(check_process)) + logger.error("%s process not found in json.", str(check_process)) return False return True diff --git a/profiler/advisor/advisor_backend/compute_advice/npu_fused/csv_analyzer.py b/profiler/advisor/advisor_backend/compute_advice/npu_fused/csv_analyzer.py index 8ae109856..d29bfafc4 100644 --- a/profiler/advisor/advisor_backend/compute_advice/npu_fused/csv_analyzer.py +++ b/profiler/advisor/advisor_backend/compute_advice/npu_fused/csv_analyzer.py @@ -13,36 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -import multiprocessing - import pandas as pd -import numpy as np from common_func.path_manager import PathManager from common_func_advisor.constant import Constant -from .op_perf import OpPerfFactory class CSVAnalyzer: def __init__(self, path) -> None: self._path = path - def process(self): - PathManager.check_path_readable(self._path) - df = pd.read_csv(self._path, dtype={"Start Time(us)": str}) - # 分析是否存在可融合的算子 - op_type_list = df["Type"].tolist() - duration_list = df["Duration(us)"].tolist() - start_times = df["Start Time(us)"].tolist() - # 去除末尾的\t分隔符 - start_times = [start_time[:-1] for start_time in start_times] - result_list = [] - for pattern in Constant.PATTERN_DICT.keys(): - result_list.extend(self.find_all_sub_lists(op_type_list, duration_list, start_times, pattern)) - data_frame = pd.DataFrame(result_list) - data_frame.columns = ["pattern_name", "pattern", "len", "count", "duration sum(us)", "op durations(us)", - "index", "first_timestamp"] - return data_frame @staticmethod def find_all_sub_lists(op_type_list, duration_list, start_times, expect_sub_list): @@ -81,3 +61,20 @@ class CSVAnalyzer: repeated_sublists.append([pattern_name, expect_sub_list, 0, 0, 0, 0, 0, 0]) # 返回所有重复的子列表 return repeated_sublists + + def process(self): + PathManager.check_path_readable(self._path) + df = pd.read_csv(self._path, dtype={"Start Time(us)": str}) + # 分析是否存在可融合的算子 + op_type_list = df["Type"].tolist() + duration_list = df["Duration(us)"].tolist() + start_times = df["Start Time(us)"].tolist() + # 去除末尾的\t分隔符 + start_times = [start_time[:-1] for start_time in start_times] + result_list = [] + for pattern in Constant.PATTERN_DICT.keys(): + result_list.extend(self.find_all_sub_lists(op_type_list, duration_list, start_times, pattern)) + data_frame = pd.DataFrame(result_list) + data_frame.columns = ["pattern_name", "pattern", "len", "count", "duration sum(us)", "op durations(us)", + "index", "first_timestamp"] + return data_frame diff --git a/profiler/advisor/advisor_backend/compute_advice/npu_fused/op_perf.py b/profiler/advisor/advisor_backend/compute_advice/npu_fused/op_perf.py index 7bcbed5a7..87ed2bac3 100644 --- a/profiler/advisor/advisor_backend/compute_advice/npu_fused/op_perf.py +++ b/profiler/advisor/advisor_backend/compute_advice/npu_fused/op_perf.py @@ -85,15 +85,15 @@ class OpPerf: self.aiv_mte3_ratio = op_row.get("aiv_mte3_ratio") self.aiv_icache_miss_rate = op_row.get("aiv_icache_miss_rate") self.cube_utilization = op_row.get("cube_utilization( %)") - + @staticmethod def get_dtype_size(dtype_str: str): return Constant.DTYPE_SIZE_MAP.get(dtype_str.lower(), 0) - + @staticmethod def get_element_count(shape: list): return functools.reduce(lambda x, y: int(x) * int(y), shape) - + @staticmethod def shape_to_tuple(shape_str: str) -> tuple: if not isinstance(shape_str, str): @@ -110,7 +110,7 @@ class OpPerf: elements = tuple(int(element) if "" != element else 0 for element in elements) shape_result.append(elements) return tuple(shape_result) - + @staticmethod def dtype_to_tuple(dtypes_str: str) -> tuple: if not isinstance(dtypes_str, str): @@ -121,10 +121,10 @@ class OpPerf: return [] pairs = split_dtypes.split(';') return tuple(pairs) - + def get_mac_ratio(self): return self.aic_mac_ratio - + def get_size(self, shapes_str, dtypes_str): shapes = self.shape_to_tuple(shapes_str) dtypes = self.dtype_to_tuple(dtypes_str) @@ -140,7 +140,7 @@ class OpPerf: dtype_size = self.get_dtype_size(dtypes[index]) all_size += element_count * dtype_size return all_size - + def get_calc_size(self): # input and output bytes (MB) if not self.input_shapes or not self.output_shapes: @@ -149,14 +149,16 @@ class OpPerf: intput_size = self.get_size(self.input_shapes, self.input_data_types) output_size = self.get_size(self.output_shapes, self.output_data_types) return (intput_size + output_size) / (Constant.BYTE_UNIT_TRANS * Constant.BYTE_UNIT_TRANS) - + def get_throughput(self): - # throughput(GB/s) + # throughput bytes (GB/s) if not self.task_duration or abs(self.task_duration) < 1e-6: print("[ERROR] There is no task_duration, do not assess vector op performance.") return 0 - return self.row[Constant.TITLE.SIZE] / Constant.BYTE_UNIT_TRANS / self.task_duration * Constant.UNIT_TRANS * Constant.UNIT_TRANS - + return (self.row[ + Constant.TITLE.SIZE] / Constant.BYTE_UNIT_TRANS + / self.task_duration * Constant.UNIT_TRANS * Constant.UNIT_TRANS) + def get_perf_color(self): return PerfColor.WHITE diff --git a/profiler/advisor/advisor_backend/logger.py b/profiler/advisor/advisor_backend/logger.py index 3b72d157a..5fb8b5d40 100644 --- a/profiler/advisor/advisor_backend/logger.py +++ b/profiler/advisor/advisor_backend/logger.py @@ -15,6 +15,7 @@ import logging + class Logger: def __init__(self): if not hasattr(self, 'logger'): @@ -35,4 +36,3 @@ class Logger: def critical(self, message): self.logger.critical(message) - diff --git a/profiler/advisor/advisor_backend/timeline_advice/op_schedule_advice.py b/profiler/advisor/advisor_backend/timeline_advice/op_schedule_advice.py index 9e868784c..5bf2fed3c 100644 --- a/profiler/advisor/advisor_backend/timeline_advice/op_schedule_advice.py +++ b/profiler/advisor/advisor_backend/timeline_advice/op_schedule_advice.py @@ -14,11 +14,13 @@ # limitations under the License. import logging from decimal import Decimal + from common_func_advisor.constant import Constant from timeline_advice.timeline_advice_base import TimelineAdviceBase logger = logging.getLogger() + class OpScheduleAdvice(TimelineAdviceBase): def __init__(self, collection_path: str): super().__init__(collection_path) @@ -46,7 +48,7 @@ class OpScheduleAdvice(TimelineAdviceBase): merge_data = list() merge_data.extend(cpt_data) merge_data.extend(free_data) - merge_data.sort(key=lambda x : Decimal(x.get("ts"))) + merge_data.sort(key=lambda x: Decimal(x.get("ts"))) idx = free_idx = 0 while idx < len(merge_data) and free_idx < len(op_free): entry = merge_data[idx] @@ -62,10 +64,10 @@ class OpScheduleAdvice(TimelineAdviceBase): if free_ratio < 0.2: return self.cur_bottleneck = f"NPU Utilication: {round(free_ratio * 100, 2)}%, " \ - f"NPU Free Utilization: {round(cpt_ratio * 100, 2)}%." + f"NPU Free Utilization: {round(cpt_ratio * 100, 2)}%." if len(self.preparse_data[self.PreParseType.SYNCHRONIZE]) > 1: self.cur_advice = f"Device synchronize {len(self.preparse_data[self.PreParseType.SYNCHRONIZE])} times, " \ - "try to reduce synchronization statements to alleviate the bottleneck of operator delivery.\n" + "try to reduce synchronization statements to alleviate the bottleneck of operator delivery.\n" small_op_num = self.small_op_block(op_free, op_dur) small_op_ratio = small_op_num / len(op_dur) if op_dur else 0.0 if small_op_ratio > Constant.SMALL_OP_NUM_RATIO: diff --git a/profiler/advisor/advisor_backend/timeline_advice/timeline_advice_base.py b/profiler/advisor/advisor_backend/timeline_advice/timeline_advice_base.py index 0bcf9a8f6..9fac56e73 100644 --- a/profiler/advisor/advisor_backend/timeline_advice/timeline_advice_base.py +++ b/profiler/advisor/advisor_backend/timeline_advice/timeline_advice_base.py @@ -13,18 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging +import os from abc import abstractmethod from collections import defaultdict -import json -import os -import logging from advice_base import AdviceBase + from profiler.prof_common.file_manager import FileManager logger = logging.getLogger() logger.setLevel(logging.INFO) + class TimelineAdviceBase(AdviceBase): class PreParseType: OPTIMIZER = 0 @@ -53,21 +54,21 @@ class TimelineAdviceBase(AdviceBase): check whether input path is valid """ if not os.path.exists(self.collection_path): - logger.error("Path: %s is not exist.",str(self.collection_path)) + logger.error("Path: %s is not exist.", str(self.collection_path)) return False if os.path.isdir(self.collection_path) and \ - (self.collection_path.endswith("ascend_pt") or self.collection_path.endswith("ascend_ms")): + (self.collection_path.endswith("ascend_pt") or self.collection_path.endswith("ascend_ms")): self.trace_view_path = os.path.join(self.collection_path, "ASCEND_PROFILER_OUTPUT", "trace_view.json") if not os.path.exists(self.trace_view_path): - logger.error("trace_view.json is not exist in the Path: %s."\ - ,str(os.path.join(self.collection_path, "ASCEND_PROFILER_OUTPUT"))) + logger.error("trace_view.json is not exist in the Path: %s.", + str(os.path.join(self.collection_path, "ASCEND_PROFILER_OUTPUT"))) return False elif os.path.isfile(self.collection_path) and os.path.basename(self.collection_path) == "trace_view.json": self.trace_view_path = self.collection_path else: logger.error("Please input ascend_pt or trace_view.json.") return False - logger.info("Start to analyse the target file: %s",str(self.trace_view_path)) + logger.info("Start to analyse the target file: %s", str(self.trace_view_path)) return True @abstractmethod diff --git a/profiler/advisor/analyzer/analyzer_controller.py b/profiler/advisor/analyzer/analyzer_controller.py index 879b04ecb..b239748ba 100644 --- a/profiler/advisor/analyzer/analyzer_controller.py +++ b/profiler/advisor/analyzer/analyzer_controller.py @@ -309,20 +309,20 @@ class AnalyzerController: output_path: analysis output path(including html and xlsx). Example: - >>> # initialize a global analyzer controller - >>> analyzer_controller = AnalyzerController() - >>> analysis_kwargs = dict(advisor_analyze_processes=2, disable_profiling_comparison=True) - >>> - >>> async_analysis_process = analyzer_controller.async_do_analysis(Interface.all_dimension, **analysis_kwargs) - >>> - >>> # query the job status every second - >>> while True: - >>> response = analyzer_controller.get_response_by_pid(async_analysis_process.pid) - >>> print(f'analysis response is {response}') - >>> if response.get("status") in ["success", "failed"]: - >>> async_analysis_process.join() - >>> break - >>> time.sleep(1) + >>> # initialize a global analyzer controller + >>> analyzer_controller = AnalyzerController() + >>> analysis_kwargs = dict(advisor_analyze_processes=2, disable_profiling_comparison=True) + >>> + >>> async_analysis_process = analyzer_controller.async_do_analysis(Interface.all_dimension, **analysis_kwargs) + >>> + >>> # query the job status every second + >>> while True: + >>> response = analyzer_controller.get_response_by_pid(async_analysis_process.pid) + >>> print(f'analysis response is {response}') + >>> if response.get("status") in ["success", "failed"]: + >>> async_analysis_process.join() + >>> break + >>> time.sleep(1) """ kwargs["is_async_analysis"] = True diff --git a/profiler/advisor/analyzer/base_analyzer.py b/profiler/advisor/analyzer/base_analyzer.py index 17fa7fd3f..0391eb88a 100644 --- a/profiler/advisor/analyzer/base_analyzer.py +++ b/profiler/advisor/analyzer/base_analyzer.py @@ -114,12 +114,12 @@ class BaseAnalyzer(VersionControl, metaclass=ABCMeta): else: for _, dirs, __ in os.walk(self.collection_path): is_found_type = False - for dir in dirs: - if dir.endswith(ASCEND_MS): + for direction in dirs: + if direction.endswith(ASCEND_MS): profiling_type = [elem for elem in profiling_type_list if Constant.MINDSPORE in elem][0] is_found_type = True break - elif dir.endswith(ASCEND_PT): + elif direction.endswith(ASCEND_PT): profiling_type = [elem for elem in profiling_type_list if Constant.PYTORCH in elem][0] is_found_type = True break @@ -142,9 +142,9 @@ class BaseAnalyzer(VersionControl, metaclass=ABCMeta): ascend_dirs.append(self.collection_path) else: for root, dirs, _ in os.walk(self.collection_path): - for dir in dirs: - if dir.endswith(ASCEND_MS): - ascend_dirs.append(os.path.join(root, dir)) + for direction in dirs: + if direction.endswith(ASCEND_MS): + ascend_dirs.append(os.path.join(root, direction)) if ascend_dirs: ascend_dir = ascend_dirs[0] for file_name in os.listdir(ascend_dir): diff --git a/profiler/advisor/analyzer/computation/ai_core_freq/ai_core_freq_checker.py b/profiler/advisor/analyzer/computation/ai_core_freq/ai_core_freq_checker.py index 798d41e16..c2b7f246b 100644 --- a/profiler/advisor/analyzer/computation/ai_core_freq/ai_core_freq_checker.py +++ b/profiler/advisor/analyzer/computation/ai_core_freq/ai_core_freq_checker.py @@ -75,9 +75,9 @@ class AICoreFreqChecker: if self.decrease_freq_ops: # 按算子总耗时和降频比率 降序排列 - self.decrease_freq_ops.sort(key = + self.decrease_freq_ops.sort(key= lambda x: (x[self.TOTAL_DURATION_INDEX], x[self.DECREASE_FREQ_RATIO_INDEX]), - reverse = True) + reverse=True) if not self.ai_core_freq_issues: return diff --git a/profiler/advisor/analyzer/computation/aicpu/aicpu_checker.py b/profiler/advisor/analyzer/computation/aicpu/aicpu_checker.py index 83f3b6acc..fc8130f7a 100644 --- a/profiler/advisor/analyzer/computation/aicpu/aicpu_checker.py +++ b/profiler/advisor/analyzer/computation/aicpu/aicpu_checker.py @@ -23,8 +23,9 @@ from profiler.advisor.dataset.dataset import Dataset from profiler.advisor.dataset.profiling.profiling_dataset import ProfilingDataset from profiler.advisor.dataset.timeline_event_dataset import ComputationAnalysisDataset from profiler.prof_common.additional_args_manager import AdditionalArgsManager -from profiler.prof_common.file_manager import FileManager from profiler.prof_common.constant import Constant +from profiler.prof_common.file_manager import FileManager + class AicpuChecker(OperatorChecker): _CHECKER = "aicpu operator" diff --git a/profiler/advisor/analyzer/computation/operator_checker.py b/profiler/advisor/analyzer/computation/operator_checker.py index 445f09cff..1b03dd25f 100644 --- a/profiler/advisor/analyzer/computation/operator_checker.py +++ b/profiler/advisor/analyzer/computation/operator_checker.py @@ -134,23 +134,6 @@ class OperatorChecker(VersionControl): ) return OptimizeRecord(optimization_item, statistics_item) - def _get_description(self, description, op_type_list=None): - if not op_type_list: - return description - - desc_suffix = [] - for i, _ in enumerate(op_type_list): - if i % 3 == 0 and i != 0: - desc_suffix.append("\n") - - desc_suffix.append(f"{op_type_list[i]}") - - if i < len(op_type_list) - 1: - desc_suffix.append(", ") - - description += "".join(desc_suffix) - return description - def pre_check(self, profiling_data) -> bool: return True @@ -335,3 +318,20 @@ class OperatorChecker(VersionControl): logger.warning(self.SKIP_CHECK_MSG, self._CHECKER, "op summary") return False return True + + def _get_description(self, description, op_type_list=None): + if not op_type_list: + return description + + desc_suffix = [] + for i, _ in enumerate(op_type_list): + if i % 3 == 0 and i != 0: + desc_suffix.append("\n") + + desc_suffix.append(f"{op_type_list[i]}") + + if i < len(op_type_list) - 1: + desc_suffix.append(", ") + + description += "".join(desc_suffix) + return description diff --git a/profiler/advisor/analyzer/computation/profiling_analyzer.py b/profiler/advisor/analyzer/computation/profiling_analyzer.py index 97154bb9f..ccf671139 100644 --- a/profiler/advisor/analyzer/computation/profiling_analyzer.py +++ b/profiler/advisor/analyzer/computation/profiling_analyzer.py @@ -78,7 +78,7 @@ class ProfilingAnalyzer(BaseAnalyzer, ABC): return self.result - def get_priority(self,max_mem_op_dur): + def get_priority(self, max_mem_op_dur): if "aicpu" not in max_mem_op_dur.__class__.__name__.lower(): return PriorityBackgroundColor.low diff --git a/profiler/advisor/analyzer/memory/memory_analyzer.py b/profiler/advisor/analyzer/memory/memory_analyzer.py index 939e2de90..a6ad3258d 100644 --- a/profiler/advisor/analyzer/memory/memory_analyzer.py +++ b/profiler/advisor/analyzer/memory/memory_analyzer.py @@ -15,11 +15,11 @@ import logging from profiler.advisor.analyzer.base_analyzer import BaseAnalyzer -from profiler.advisor.result.result import OptimizeResult from profiler.advisor.analyzer.memory.memory_checker import MemoryOpsChecker -from profiler.advisor.display.html.render import HTMLRender from profiler.advisor.dataset.timeline_event_dataset import ScheduleAnalysisDataset from profiler.advisor.display.html.priority_background_color import PriorityBackgroundColor +from profiler.advisor.display.html.render import HTMLRender +from profiler.advisor.result.result import OptimizeResult logger = logging.getLogger() @@ -39,7 +39,8 @@ class MemoryAnalyzer(BaseAnalyzer): memory_checker = MemoryOpsChecker() memory_checker.check_memory_ops(self.dataset) memory_checker.make_record(self.result) - memory_checker.make_render(self.html_render, priority=self.get_priority(memory_checker.max_mem_op_dur), rank=kwargs.get("rank")) + memory_checker.make_render(self.html_render, priority=self.get_priority(memory_checker.max_mem_op_dur), + rank=kwargs.get("rank")) return self.result def get_priority(self, max_mem_op_dur): diff --git a/profiler/advisor/analyzer/schedule/fusion_ops/fusion_ops_analyzer.py b/profiler/advisor/analyzer/schedule/fusion_ops/fusion_ops_analyzer.py index 21cf5c4d0..47df4b476 100644 --- a/profiler/advisor/analyzer/schedule/fusion_ops/fusion_ops_analyzer.py +++ b/profiler/advisor/analyzer/schedule/fusion_ops/fusion_ops_analyzer.py @@ -12,25 +12,23 @@ # 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. -import os -import multiprocessing import logging +import multiprocessing +import os import re from tqdm import tqdm from profiler.advisor.analyzer.base_analyzer import BaseAnalyzer -from profiler.advisor.display.prompt.base_prompt import BasePrompt -from profiler.prof_common.constant import Constant -from profiler.advisor.common.analyzer_scopes import SupportedScopes from profiler.advisor.common.timeline.event import TimelineEvent +from profiler.advisor.common.timeline.fusion_ops_db import init_timeline_ops_db from profiler.advisor.config.config import Config from profiler.advisor.dataset.timeline_event_dataset import ScheduleAnalysisDataset +from profiler.advisor.display.html.priority_background_color import PriorityBackgroundColor +from profiler.advisor.display.prompt.base_prompt import BasePrompt from profiler.advisor.result.item import OptimizeItem, OptimizeRecord from profiler.advisor.utils.utils import format_timeline_result -from profiler.advisor.common.timeline.fusion_ops_db import init_timeline_ops_db -from profiler.advisor.display.html.priority_background_color import PriorityBackgroundColor -from profiler.prof_common.additional_args_manager import AdditionalArgsManager +from profiler.prof_common.constant import Constant logger = logging.getLogger() @@ -96,11 +94,11 @@ class TimelineFusionOpsAnalyzer(BaseAnalyzer): """ if not self.matched_op_stacks: return - + prompt_class = BasePrompt.get_prompt_class(self.__class__.__name__) desc = prompt_class.DESCRIPTION.format(self.cann_version, self.profiling_version, - len(format_timeline_result(self.matched_op_stacks))) + len(format_timeline_result(self.matched_op_stacks))) suggestion = prompt_class.SUGGESTION if self.empty_stacks: desc += prompt_class.EMPTY_STACK_DESCRIPTION @@ -210,7 +208,8 @@ class TimelineFusionOpsAnalyzer(BaseAnalyzer): # by the regex index and then calculate the real index for matched fusion operators in event dataset for left, right in zip(total_ops_split_points, total_ops_split_points[1:]): matched_op_flag = True if (left, right) in matched_pattern_index_tuple else False - matched_ops_list = total_op_name[left: right].strip(Constant.OP_SEP).split(Constant.OP_SEP + Constant.OP_SEP) + matched_ops_list = ( + total_op_name[left: right].strip(Constant.OP_SEP).split(Constant.OP_SEP + Constant.OP_SEP)) op_index.append([matched_op_flag, len(matched_ops_list)]) for i, _ in enumerate(op_index): if i > 0: diff --git a/profiler/advisor/common/analyzer_scopes.py b/profiler/advisor/common/analyzer_scopes.py index 2cad1a3ce..c68acd5ed 100644 --- a/profiler/advisor/common/analyzer_scopes.py +++ b/profiler/advisor/common/analyzer_scopes.py @@ -12,8 +12,9 @@ # 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. -class SupportedScopes: + +class SupportedScopes: # used for specify fourth-level commands and define the key of the result dict # the key defined bellow must be the same as value TIMELINE_FUSION_OPS = "timeline_fusion_ops" diff --git a/profiler/advisor/common/async_analysis_status.py b/profiler/advisor/common/async_analysis_status.py index 3d9a5d7c1..ee64ebf4d 100644 --- a/profiler/advisor/common/async_analysis_status.py +++ b/profiler/advisor/common/async_analysis_status.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ # 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. -""" class AsyncAnalysisStatus: diff --git a/profiler/advisor/common/enum_params_parser.py b/profiler/advisor/common/enum_params_parser.py index e9a3f6639..b12937d9d 100644 --- a/profiler/advisor/common/enum_params_parser.py +++ b/profiler/advisor/common/enum_params_parser.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,9 +13,9 @@ # 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. -""" -import os + import logging +import os import typing from profiler.advisor.common.timeline.event import AdvisorDict diff --git a/profiler/advisor/common/graph/graph.py b/profiler/advisor/common/graph/graph.py index f86f5db7f..00f2fd5ad 100644 --- a/profiler/advisor/common/graph/graph.py +++ b/profiler/advisor/common/graph/graph.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,9 +13,9 @@ # 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. -""" + import logging -from typing import Dict, List, Tuple, Callable, Any, Optional, Union +from typing import Dict, List, Tuple, Optional, Union import networkx as nx @@ -33,7 +33,7 @@ class Graph: def __init__(self, nodes: Dict[str, Optional[Union[HostGraphNode, QueryGraphNode]]] = None, edges: List[Tuple[Optional[Union[HostGraphNode, QueryGraphNode]], - Optional[Union[HostGraphNode, QueryGraphNode]]]] = None, + Optional[Union[HostGraphNode, QueryGraphNode]]]] = None, name: str = None): self.name = name self.graph = nx.DiGraph(name=name) diff --git a/profiler/advisor/common/graph/graph_match.py b/profiler/advisor/common/graph/graph_match.py index fbf0a8abe..f4e863049 100644 --- a/profiler/advisor/common/graph/graph_match.py +++ b/profiler/advisor/common/graph/graph_match.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,11 +13,11 @@ # 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. -""" + import itertools import logging -from functools import lru_cache from collections import deque +from functools import lru_cache from typing import Dict, Generator, List, Callable, Hashable, Tuple import networkx as nx diff --git a/profiler/advisor/common/graph/graph_parser.py b/profiler/advisor/common/graph/graph_parser.py index cffbfa19b..718c6af48 100644 --- a/profiler/advisor/common/graph/graph_parser.py +++ b/profiler/advisor/common/graph/graph_parser.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,16 +13,16 @@ # 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. -""" -import os -import logging + import itertools +import logging +import os from collections import deque from dataclasses import dataclass from typing import List, Tuple, Dict -from profiler.prof_common.file_manager import FileManager from profiler.advisor.utils.file import FileOpen +from profiler.prof_common.file_manager import FileManager logger = logging.getLogger() @@ -95,6 +95,7 @@ class HostGraphParser: """ Parse graph metadata from text file """ + def __init__(self, file_path): self.buffer = deque(maxlen=100) self.line_no = 0 @@ -303,7 +304,7 @@ class QueryGraphNode: def __eq__(self, other): return self._op_type == other._op_type and \ - self._id == other._id + self._id == other._id def __hash__(self): return hash(self._op_type + str(self._id)) diff --git a/profiler/advisor/common/profiling/ge_info.py b/profiler/advisor/common/profiling/ge_info.py index 91642f967..927f5d3e7 100644 --- a/profiler/advisor/common/profiling/ge_info.py +++ b/profiler/advisor/common/profiling/ge_info.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ # 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. -""" + import logging import os from typing import Any, List diff --git a/profiler/advisor/common/profiling/msprof.py b/profiler/advisor/common/profiling/msprof.py index 54206bb47..76f51b672 100644 --- a/profiler/advisor/common/profiling/msprof.py +++ b/profiler/advisor/common/profiling/msprof.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ # 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. -""" + import logging from typing import Dict, List diff --git a/profiler/advisor/common/profiling/op_summary.py b/profiler/advisor/common/profiling/op_summary.py index c042509df..b1b416888 100644 --- a/profiler/advisor/common/profiling/op_summary.py +++ b/profiler/advisor/common/profiling/op_summary.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ # 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. -""" + import logging from decimal import Decimal from typing import List, Any diff --git a/profiler/advisor/common/profiling/tasktime.py b/profiler/advisor/common/profiling/tasktime.py index 211800585..e4bc691ff 100644 --- a/profiler/advisor/common/profiling/tasktime.py +++ b/profiler/advisor/common/profiling/tasktime.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,7 +13,8 @@ # 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. -""" + + import logging from typing import Dict, List diff --git a/profiler/advisor/common/version_control.py b/profiler/advisor/common/version_control.py index ec30b3be9..e95168dec 100644 --- a/profiler/advisor/common/version_control.py +++ b/profiler/advisor/common/version_control.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" + # Copyright (C) 2024-2024. Huawei Technologies Co., Ltd. All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ # 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. -""" + import logging from typing import List diff --git a/profiler/advisor/config/config.py b/profiler/advisor/config/config.py index 0fbca3e57..518cddb88 100644 --- a/profiler/advisor/config/config.py +++ b/profiler/advisor/config/config.py @@ -149,11 +149,6 @@ class Config: except Exception: return "" - def _normalize_path(self, file) -> str: - if not file.startswith("/"): - file = os.path.join(self._work_path, file) - return os.path.abspath(file) - def set_config(self, key, value) -> None: """ set config value @@ -176,11 +171,15 @@ class Config: def set_log_path(self, result_file: str, log_path: str = None): self.log_path = log_path if log_path is not None else os.path.join(self._work_path, "log") os.makedirs(self.log_path, exist_ok=True) - self.config.set("ANALYSE","analysis_result_file",os.path.join(self.log_path, result_file)) + self.config.set("ANALYSE", "analysis_result_file",os.path.join(self.log_path, result_file)) self._analysis_result_file = os.path.join(self.log_path, result_file) def remove_log(self): if self.log_path and os.path.isdir(self.log_path) and not os.listdir(self.log_path): os.rmdir(self.log_path) + def _normalize_path(self, file) -> str: + if not file.startswith("/"): + file = os.path.join(self._work_path, file) + return os.path.abspath(file) diff --git a/profiler/advisor/dataset/profiling/device_info.py b/profiler/advisor/dataset/profiling/device_info.py index 625e4ed0a..dc2eebec1 100644 --- a/profiler/advisor/dataset/profiling/device_info.py +++ b/profiler/advisor/dataset/profiling/device_info.py @@ -1,3 +1,18 @@ +# Copyright (c) 2024, Huawei Technologies Co., Ltd. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """ profiling info """ @@ -23,19 +38,6 @@ class DeviceInfoParser: def __init__(self, path) -> None: self._path = path - def parse_data(self) -> bool: - """ - parse profiling data - :return: true for success or false - """ - file_list = get_file_path_from_directory(self._path, lambda x: x.startswith("info.json.")) - if not file_list: - return False - for info in file_list: - if self._parse(info): - return True - return False - @staticmethod def _parse(info_file: str) -> bool: if info_file.endswith("done"): @@ -61,3 +63,18 @@ class DeviceInfoParser: return True logger.error("No ai_core_num in json info file %s", info_file) return False + + def parse_data(self) -> bool: + """ + parse profiling data + :return: true for success or false + """ + file_list = get_file_path_from_directory(self._path, lambda x: x.startswith("info.json.")) + if not file_list: + return False + for info in file_list: + if self._parse(info): + return True + return False + + diff --git a/profiler/advisor/dataset/profiling/profiling_dataset.py b/profiler/advisor/dataset/profiling/profiling_dataset.py index cd1d953aa..de2583c8c 100644 --- a/profiler/advisor/dataset/profiling/profiling_dataset.py +++ b/profiler/advisor/dataset/profiling/profiling_dataset.py @@ -16,19 +16,13 @@ import logging import os -import yaml -from profiler.prof_common.constant import Constant -from profiler.advisor.common.profiling.ge_info import GeInfo -from profiler.advisor.common.profiling.msprof import Msprof -from profiler.advisor.common.profiling.op_summary import OpSummary -from profiler.advisor.common.profiling.tasktime import TaskTime from profiler.advisor.common.enum_params_parser import EnumParamsParser from profiler.advisor.dataset.dataset import Dataset from profiler.advisor.dataset.profiling.device_info import DeviceInfoParser from profiler.advisor.utils.utils import join_prof_path +from profiler.prof_common.constant import Constant from profiler.prof_common.file_manager import FileManager - logger = logging.getLogger() @@ -37,7 +31,8 @@ class ProfilingDataset(Dataset): def __init__(self, collection_path, data: dict, **kwargs) -> None: self.cann_version = kwargs.get(Constant.CANN_VERSION, EnumParamsParser().get_default(Constant.CANN_VERSION)) - self.prof_type = kwargs.get(Constant.PROFILING_TYPE_UNDER_LINE, EnumParamsParser().get_default(Constant.PROFILING_TYPE_UNDER_LINE)) + self.prof_type = kwargs.get(Constant.PROFILING_TYPE_UNDER_LINE, + EnumParamsParser().get_default(Constant.PROFILING_TYPE_UNDER_LINE)) self.patterns = self.parse_pattern() self.current_version_pattern = self.get_current_version_pattern() self._info = None @@ -66,10 +61,10 @@ class ProfilingDataset(Dataset): if is_success: setattr(self, item, data_object) elif current_path: - logger.info("Skip parse %s with file pattern %s from local path %s", + logger.info("Skip parse %s with file pattern %s from local path %s", self.current_version_pattern.get('class_attr').get(item), file_pattern_list, current_path - ) + ) else: logger.warning(f"Unsupported arguments : %s to build %s", dirs_pattern, self.__class__.__name__) @@ -83,7 +78,7 @@ class ProfilingDataset(Dataset): if not os.path.isabs(config_path): config_path = os.path.join(os.path.dirname(__file__), - "../", "../", config_path) + "../", "../", config_path) if not os.path.exists(config_path): logger.warning("Skip parse profiling dataset, because %s does not exist.", config_path) @@ -96,7 +91,7 @@ class ProfilingDataset(Dataset): def collection_path(self): """collection_path""" return self.collection_path - + def _parse(self): info = DeviceInfoParser(self.collection_path) if info.parse_data(): diff --git a/profiler/advisor/dataset/profiling/profiling_parser.py b/profiler/advisor/dataset/profiling/profiling_parser.py index 73c9fc013..26ab640e4 100644 --- a/profiler/advisor/dataset/profiling/profiling_parser.py +++ b/profiler/advisor/dataset/profiling/profiling_parser.py @@ -59,28 +59,6 @@ class ProfilingParser: except (FloatingPointError, ValueError): return 0.0 - @staticmethod - def _check_csv_file_format(csv_file_name: str, csv_content: List[List[str]]): - if not csv_content: - logger.error("%s is empty", csv_file_name) - return False - return True - - @staticmethod - def _get_csv_title(data: List, number=0, title_index=0): - """ - number = 0 replace (us) (ns).. - other replace " " to "_" - title_index: position of title default 0 - """ - title_dict: Dict[int, str] = {} - for idx, title in enumerate(data[title_index]): - if number == 0: - title_dict[idx] = format_excel_title(title) - else: - title_dict[idx] = title.replace(" ", "_") - return title_dict - @staticmethod def parse_from_file(file): """ @@ -104,6 +82,28 @@ class ProfilingParser: """ return self._filename, self._raw_data + @staticmethod + def _check_csv_file_format(csv_file_name: str, csv_content: List[List[str]]): + if not csv_content: + logger.error("%s is empty", csv_file_name) + return False + return True + + @staticmethod + def _get_csv_title(data: List, number=0, title_index=0): + """ + number = 0 replace (us) (ns).. + other replace " " to "_" + title_index: position of title default 0 + """ + title_dict: Dict[int, str] = {} + for idx, title in enumerate(data[title_index]): + if number == 0: + title_dict[idx] = format_excel_title(title) + else: + title_dict[idx] = title.replace(" ", "_") + return title_dict + def _parse_from_file(self): if not isinstance(self.file_pattern_list, list): self.file_pattern_list = [self.file_pattern_list] diff --git a/profiler/advisor/dataset/timeline_event_dataset.py b/profiler/advisor/dataset/timeline_event_dataset.py index fbac2c5bf..aad763191 100644 --- a/profiler/advisor/dataset/timeline_event_dataset.py +++ b/profiler/advisor/dataset/timeline_event_dataset.py @@ -81,8 +81,9 @@ class BaseTimelineEventDataset: if func_name == FrequencyCollector.__name__: ops_with_task_type = getattr(self, "ops_with_task_type", {}).values() kwargs["ai_core_ops"] = [ - op for op in ops_with_task_type if - op.get(Constant.TASK_TYPE) in [Constant.AI_CORE, Constant.MIX_AIC] + op + for op in ops_with_task_type + if op.get(Constant.TASK_TYPE) in [Constant.AI_CORE, Constant.MIX_AIC] ] return kwargs diff --git a/profiler/advisor/dataset/timeline_op_collector/timeline_op_collector.py b/profiler/advisor/dataset/timeline_op_collector/timeline_op_collector.py index d868acbf5..fb6a5b1c0 100644 --- a/profiler/advisor/dataset/timeline_op_collector/timeline_op_collector.py +++ b/profiler/advisor/dataset/timeline_op_collector/timeline_op_collector.py @@ -1,3 +1,18 @@ +# Copyright (c) 2024, Huawei Technologies Co., Ltd. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import logging import math import os diff --git a/profiler/advisor/display/prompt/base_prompt.py b/profiler/advisor/display/prompt/base_prompt.py index 2dcacc381..2a538f3db 100644 --- a/profiler/advisor/display/prompt/base_prompt.py +++ b/profiler/advisor/display/prompt/base_prompt.py @@ -16,6 +16,7 @@ import importlib from profiler.prof_common.additional_args_manager import AdditionalArgsManager + def split_camel_case(word): result = [] current_word = [] diff --git a/profiler/advisor/display/prompt/en/dynamic_shape_prompt.py b/profiler/advisor/display/prompt/en/dynamic_shape_prompt.py index b350f603f..36e6f5e52 100644 --- a/profiler/advisor/display/prompt/en/dynamic_shape_prompt.py +++ b/profiler/advisor/display/prompt/en/dynamic_shape_prompt.py @@ -17,8 +17,8 @@ class DynamicShapePrompt(object): RANK_ID = "RANK {} " PROBLEM = "Dynamic Shape Operator" DESCRIPTION = "Found all operators are dynamic shape" - ENABLE_COMPILED_SUGGESTION = "1. Please try to set environment by execute `export HOST_CACHE_CAPACITY=20`.\n." \ - "2. Please place the following code at the entrance of the python script to disable jit compile.\n " \ - "Code: `torch_npu.npu.set_compile_mode(jit_compile=False) \n " \ - "torch_npu.npu.config.allow_internal_format = False`.\n" + ENABLE_COMPILED_SUGGESTION = """1. Please try to set environment by execute `export HOST_CACHE_CAPACITY=20`.\n. + 2. Please place the following code at the entrance of the python script to disable jit compile.\n + Code: `torch_npu.npu.set_compile_mode(jit_compile=False) + torch_npu.npu.config.allow_internal_format = False`.\n""" RELEASE_SUGGESTION = "for details please refer to link : LINK" diff --git a/profiler/advisor/utils/log.py b/profiler/advisor/utils/log.py index 5d9757ebf..b260a8492 100644 --- a/profiler/advisor/utils/log.py +++ b/profiler/advisor/utils/log.py @@ -1,3 +1,18 @@ +# Copyright (c) 2024, Huawei Technologies Co., Ltd. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """ log module """ diff --git a/profiler/advisor/utils/tools.py b/profiler/advisor/utils/tools.py index 2cbcb5e05..83291c953 100644 --- a/profiler/advisor/utils/tools.py +++ b/profiler/advisor/utils/tools.py @@ -1,3 +1,18 @@ +# Copyright (c) 2024, Huawei Technologies Co., Ltd. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + from functools import partial import click @@ -16,6 +31,12 @@ class ClickAliasedGroup(click.Group): self._alias_dict = {} self._commands = {} + def _decorator_warpper(self, decorator, alias, func=None): + cmd = decorator(func) + self._commands[cmd.name] = alias + self._alias_dict[alias] = cmd.name + return cmd + def command(self, *args, **kwargs): alias = kwargs.pop('alias', None) decorator = super(ClickAliasedGroup, self).command(*args, **kwargs) @@ -32,12 +53,6 @@ class ClickAliasedGroup(click.Group): return partial(self._decorator_warpper, decorator, alias) - def _decorator_warpper(self, decorator, alias, func=None): - cmd = decorator(func) - self._commands[cmd.name] = alias - self._alias_dict[alias] = cmd.name - return cmd - def resolve_alias(self, cmd_name): if cmd_name in self._alias_dict.keys(): return self._alias_dict[cmd_name] diff --git a/profiler/advisor/utils/utils.py b/profiler/advisor/utils/utils.py index 710084ef8..1094145b5 100644 --- a/profiler/advisor/utils/utils.py +++ b/profiler/advisor/utils/utils.py @@ -120,7 +120,8 @@ def singleton(cls): # 过滤出函数对象 function_objs = [ - member[1] for member in members + member[1] + for member in members if inspect.isfunction(member[1]) or inspect.ismethod(member[1]) ] for function_obj in function_objs: -- Gitee From 1b1688d5424bfdbea93bf8ffcd65ca56b01bf95b Mon Sep 17 00:00:00 2001 From: huxianglong Date: Mon, 23 Dec 2024 18:25:04 +0800 Subject: [PATCH 2/5] clean code --- .../dataset/profiling/profiling_parser.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/profiler/advisor/dataset/profiling/profiling_parser.py b/profiler/advisor/dataset/profiling/profiling_parser.py index 26ab640e4..c5576efc0 100644 --- a/profiler/advisor/dataset/profiling/profiling_parser.py +++ b/profiler/advisor/dataset/profiling/profiling_parser.py @@ -67,21 +67,6 @@ class ProfilingParser: # 实现解析文件的逻辑,这里可以根据需要进行扩展 return False - def parse_data(self) -> bool: - """ - Parse task time file - :return: true or false - """ - if self._parse_from_file(): - return True - return False - - def get_raw_data(self): - """ - get raw file name and data - """ - return self._filename, self._raw_data - @staticmethod def _check_csv_file_format(csv_file_name: str, csv_content: List[List[str]]): if not csv_content: @@ -104,6 +89,21 @@ class ProfilingParser: title_dict[idx] = title.replace(" ", "_") return title_dict + def parse_data(self) -> bool: + """ + Parse task time file + :return: true or false + """ + if self._parse_from_file(): + return True + return False + + def get_raw_data(self): + """ + get raw file name and data + """ + return self._filename, self._raw_data + def _parse_from_file(self): if not isinstance(self.file_pattern_list, list): self.file_pattern_list = [self.file_pattern_list] -- Gitee From 45e4073e4bfcdee7ee2def34a6d1d2d074d65c1e Mon Sep 17 00:00:00 2001 From: huxianglong Date: Mon, 23 Dec 2024 21:12:52 +0800 Subject: [PATCH 3/5] clean --- .../dataset/profiling/profiling_dataset.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/profiler/advisor/dataset/profiling/profiling_dataset.py b/profiler/advisor/dataset/profiling/profiling_dataset.py index de2583c8c..cd1d953aa 100644 --- a/profiler/advisor/dataset/profiling/profiling_dataset.py +++ b/profiler/advisor/dataset/profiling/profiling_dataset.py @@ -16,13 +16,19 @@ import logging import os +import yaml +from profiler.prof_common.constant import Constant +from profiler.advisor.common.profiling.ge_info import GeInfo +from profiler.advisor.common.profiling.msprof import Msprof +from profiler.advisor.common.profiling.op_summary import OpSummary +from profiler.advisor.common.profiling.tasktime import TaskTime from profiler.advisor.common.enum_params_parser import EnumParamsParser from profiler.advisor.dataset.dataset import Dataset from profiler.advisor.dataset.profiling.device_info import DeviceInfoParser from profiler.advisor.utils.utils import join_prof_path -from profiler.prof_common.constant import Constant from profiler.prof_common.file_manager import FileManager + logger = logging.getLogger() @@ -31,8 +37,7 @@ class ProfilingDataset(Dataset): def __init__(self, collection_path, data: dict, **kwargs) -> None: self.cann_version = kwargs.get(Constant.CANN_VERSION, EnumParamsParser().get_default(Constant.CANN_VERSION)) - self.prof_type = kwargs.get(Constant.PROFILING_TYPE_UNDER_LINE, - EnumParamsParser().get_default(Constant.PROFILING_TYPE_UNDER_LINE)) + self.prof_type = kwargs.get(Constant.PROFILING_TYPE_UNDER_LINE, EnumParamsParser().get_default(Constant.PROFILING_TYPE_UNDER_LINE)) self.patterns = self.parse_pattern() self.current_version_pattern = self.get_current_version_pattern() self._info = None @@ -61,10 +66,10 @@ class ProfilingDataset(Dataset): if is_success: setattr(self, item, data_object) elif current_path: - logger.info("Skip parse %s with file pattern %s from local path %s", + logger.info("Skip parse %s with file pattern %s from local path %s", self.current_version_pattern.get('class_attr').get(item), file_pattern_list, current_path - ) + ) else: logger.warning(f"Unsupported arguments : %s to build %s", dirs_pattern, self.__class__.__name__) @@ -78,7 +83,7 @@ class ProfilingDataset(Dataset): if not os.path.isabs(config_path): config_path = os.path.join(os.path.dirname(__file__), - "../", "../", config_path) + "../", "../", config_path) if not os.path.exists(config_path): logger.warning("Skip parse profiling dataset, because %s does not exist.", config_path) @@ -91,7 +96,7 @@ class ProfilingDataset(Dataset): def collection_path(self): """collection_path""" return self.collection_path - + def _parse(self): info = DeviceInfoParser(self.collection_path) if info.parse_data(): -- Gitee From 1478aea7c252f08bc96f0c54d4fc33ea90242f73 Mon Sep 17 00:00:00 2001 From: h30071647 Date: Mon, 23 Dec 2024 13:13:55 +0000 Subject: [PATCH 4/5] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20prof?= =?UTF-8?q?iler/advisor/dataset/profiling/profiling=5Fdataset.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dataset/profiling/profiling_dataset.py | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 profiler/advisor/dataset/profiling/profiling_dataset.py diff --git a/profiler/advisor/dataset/profiling/profiling_dataset.py b/profiler/advisor/dataset/profiling/profiling_dataset.py deleted file mode 100644 index cd1d953aa..000000000 --- a/profiler/advisor/dataset/profiling/profiling_dataset.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) 2024, Huawei Technologies Co., Ltd. -# All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import os - -import yaml -from profiler.prof_common.constant import Constant -from profiler.advisor.common.profiling.ge_info import GeInfo -from profiler.advisor.common.profiling.msprof import Msprof -from profiler.advisor.common.profiling.op_summary import OpSummary -from profiler.advisor.common.profiling.tasktime import TaskTime -from profiler.advisor.common.enum_params_parser import EnumParamsParser -from profiler.advisor.dataset.dataset import Dataset -from profiler.advisor.dataset.profiling.device_info import DeviceInfoParser -from profiler.advisor.utils.utils import join_prof_path -from profiler.prof_common.file_manager import FileManager - - -logger = logging.getLogger() - - -class ProfilingDataset(Dataset): - prof_type = "" - - def __init__(self, collection_path, data: dict, **kwargs) -> None: - self.cann_version = kwargs.get(Constant.CANN_VERSION, EnumParamsParser().get_default(Constant.CANN_VERSION)) - self.prof_type = kwargs.get(Constant.PROFILING_TYPE_UNDER_LINE, EnumParamsParser().get_default(Constant.PROFILING_TYPE_UNDER_LINE)) - self.patterns = self.parse_pattern() - self.current_version_pattern = self.get_current_version_pattern() - self._info = None - super().__init__(collection_path, data) - - def build_from_pattern(self, dirs_pattern, current_path, depth): - if depth > Constant.DEPTH_LIMIT: - logger.error("Recursion depth exceeds limit!") - return - depth += 1 - if isinstance(dirs_pattern, dict): - for key, value in dirs_pattern.items(): - self.build_from_pattern(value, join_prof_path(current_path, key), depth) - elif isinstance(dirs_pattern, list): - for item in dirs_pattern: - if hasattr(self, item) and getattr(self, item): - # 避免重复构建kernel_details.csv, op_summary.csv的数据对象 - continue - file_pattern_list = self.current_version_pattern.get('file_attr').get(item) - data_class = globals()[self.current_version_pattern.get('class_attr').get(item)] - if not hasattr(data_class, "file_pattern_list"): - continue - setattr(data_class, "file_pattern_list", self.current_version_pattern.get('file_attr').get(item)) - data_object = data_class(current_path) - is_success = data_object.parse_data() - if is_success: - setattr(self, item, data_object) - elif current_path: - logger.info("Skip parse %s with file pattern %s from local path %s", - self.current_version_pattern.get('class_attr').get(item), - file_pattern_list, current_path - ) - else: - logger.warning(f"Unsupported arguments : %s to build %s", dirs_pattern, self.__class__.__name__) - - def get_current_version_pattern(self): - for version_config_dict in self.patterns['versions']: - if version_config_dict['version'] == self.cann_version: - return version_config_dict - return dict() - - def parse_pattern(self, config_path="config/profiling_data_version_config.yaml"): - - if not os.path.isabs(config_path): - config_path = os.path.join(os.path.dirname(__file__), - "../", "../", config_path) - - if not os.path.exists(config_path): - logger.warning("Skip parse profiling dataset, because %s does not exist.", config_path) - return [] - - patterns = FileManager.read_yaml_file(config_path) - - return patterns if patterns else [] - - def collection_path(self): - """collection_path""" - return self.collection_path - - def _parse(self): - info = DeviceInfoParser(self.collection_path) - if info.parse_data(): - self._info = info - ret = False - dirs_pattern = self.current_version_pattern.get("dirs_pattern") - if dirs_pattern is not None: - self.build_from_pattern(dirs_pattern, self.collection_path, 0) - ret = True - - return ret -- Gitee From 6f4a61c37226f8fc0a7a7da8a766aebc928043d5 Mon Sep 17 00:00:00 2001 From: h30071647 Date: Mon, 23 Dec 2024 13:21:47 +0000 Subject: [PATCH 5/5] =?UTF-8?q?Revert=20"=E5=88=A0=E9=99=A4=E6=96=87?= =?UTF-8?q?=E4=BB=B6=20profiler/advisor/dataset/profiling/profiling=5Fdata?= =?UTF-8?q?set.py"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 1478aea7c252f08bc96f0c54d4fc33ea90242f73. --- .../dataset/profiling/profiling_dataset.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 profiler/advisor/dataset/profiling/profiling_dataset.py diff --git a/profiler/advisor/dataset/profiling/profiling_dataset.py b/profiler/advisor/dataset/profiling/profiling_dataset.py new file mode 100644 index 000000000..cd1d953aa --- /dev/null +++ b/profiler/advisor/dataset/profiling/profiling_dataset.py @@ -0,0 +1,110 @@ +# Copyright (c) 2024, Huawei Technologies Co., Ltd. +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import os + +import yaml +from profiler.prof_common.constant import Constant +from profiler.advisor.common.profiling.ge_info import GeInfo +from profiler.advisor.common.profiling.msprof import Msprof +from profiler.advisor.common.profiling.op_summary import OpSummary +from profiler.advisor.common.profiling.tasktime import TaskTime +from profiler.advisor.common.enum_params_parser import EnumParamsParser +from profiler.advisor.dataset.dataset import Dataset +from profiler.advisor.dataset.profiling.device_info import DeviceInfoParser +from profiler.advisor.utils.utils import join_prof_path +from profiler.prof_common.file_manager import FileManager + + +logger = logging.getLogger() + + +class ProfilingDataset(Dataset): + prof_type = "" + + def __init__(self, collection_path, data: dict, **kwargs) -> None: + self.cann_version = kwargs.get(Constant.CANN_VERSION, EnumParamsParser().get_default(Constant.CANN_VERSION)) + self.prof_type = kwargs.get(Constant.PROFILING_TYPE_UNDER_LINE, EnumParamsParser().get_default(Constant.PROFILING_TYPE_UNDER_LINE)) + self.patterns = self.parse_pattern() + self.current_version_pattern = self.get_current_version_pattern() + self._info = None + super().__init__(collection_path, data) + + def build_from_pattern(self, dirs_pattern, current_path, depth): + if depth > Constant.DEPTH_LIMIT: + logger.error("Recursion depth exceeds limit!") + return + depth += 1 + if isinstance(dirs_pattern, dict): + for key, value in dirs_pattern.items(): + self.build_from_pattern(value, join_prof_path(current_path, key), depth) + elif isinstance(dirs_pattern, list): + for item in dirs_pattern: + if hasattr(self, item) and getattr(self, item): + # 避免重复构建kernel_details.csv, op_summary.csv的数据对象 + continue + file_pattern_list = self.current_version_pattern.get('file_attr').get(item) + data_class = globals()[self.current_version_pattern.get('class_attr').get(item)] + if not hasattr(data_class, "file_pattern_list"): + continue + setattr(data_class, "file_pattern_list", self.current_version_pattern.get('file_attr').get(item)) + data_object = data_class(current_path) + is_success = data_object.parse_data() + if is_success: + setattr(self, item, data_object) + elif current_path: + logger.info("Skip parse %s with file pattern %s from local path %s", + self.current_version_pattern.get('class_attr').get(item), + file_pattern_list, current_path + ) + else: + logger.warning(f"Unsupported arguments : %s to build %s", dirs_pattern, self.__class__.__name__) + + def get_current_version_pattern(self): + for version_config_dict in self.patterns['versions']: + if version_config_dict['version'] == self.cann_version: + return version_config_dict + return dict() + + def parse_pattern(self, config_path="config/profiling_data_version_config.yaml"): + + if not os.path.isabs(config_path): + config_path = os.path.join(os.path.dirname(__file__), + "../", "../", config_path) + + if not os.path.exists(config_path): + logger.warning("Skip parse profiling dataset, because %s does not exist.", config_path) + return [] + + patterns = FileManager.read_yaml_file(config_path) + + return patterns if patterns else [] + + def collection_path(self): + """collection_path""" + return self.collection_path + + def _parse(self): + info = DeviceInfoParser(self.collection_path) + if info.parse_data(): + self._info = info + ret = False + dirs_pattern = self.current_version_pattern.get("dirs_pattern") + if dirs_pattern is not None: + self.build_from_pattern(dirs_pattern, self.collection_path, 0) + ret = True + + return ret -- Gitee