You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

analyser.py 14 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. # Copyright 2020 Huawei Technologies Co., Ltd
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # ============================================================================
  15. """The specific analyser class."""
  16. import csv
  17. import json
  18. import os
  19. from mindinsight.profiler.analyser.base_analyser import BaseAnalyser
  20. from mindinsight.profiler.common.log import logger
  21. from mindinsight.profiler.common.validator.validate_path import validate_and_normalize_path
  22. class AicoreTypeAnalyser(BaseAnalyser):
  23. """
  24. The analyser for analyzing the AICORE operator types.
  25. Args:
  26. profiling_dir (str): The directory where the parsed profiling files are
  27. located.
  28. device_id (str): The device ID.
  29. Raises:
  30. ProfilerPathErrorException: If the profiling dir is invalid.
  31. """
  32. _col_names = ['op_type', 'execution_time', 'execution_frequency', 'percent']
  33. _file_name_aicore_type_time = 'aicore_intermediate_{}_type.csv'
  34. def _load(self):
  35. """Load data according to the parsed AICORE operator types file."""
  36. op_type_file_path = os.path.join(
  37. self._profiling_dir,
  38. self._file_name_aicore_type_time.format(self._device_id)
  39. )
  40. op_type_file_path = validate_and_normalize_path(
  41. op_type_file_path, raise_key='Invalid aicore_type file path.'
  42. )
  43. if not os.path.isfile(op_type_file_path):
  44. logger.warning('The file <%s> does not exist.', op_type_file_path)
  45. return
  46. with open(op_type_file_path, 'r') as file:
  47. csv_reader = csv.reader(file)
  48. _ = next(csv_reader)
  49. for info in csv_reader:
  50. self._data.append(self._convert_field_type(info))
  51. def _filter(self, filter_condition):
  52. """
  53. Filter the profiling data according to the filter condition.
  54. Args:
  55. filter_condition (dict): The filter condition.
  56. """
  57. def _inner_filter(item: list):
  58. return self._default_filter(item, filter_condition)
  59. self._result = list(filter(_inner_filter, self._data))
  60. def _organize_query_result(self):
  61. """
  62. Organize the query result.
  63. Returns:
  64. dict, the query result.
  65. """
  66. for item in self._result:
  67. item[1] = float(format(item[1], '.6f'))
  68. return super()._organize_query_result()
  69. def _convert_field_type(self, row):
  70. """
  71. Convert the field type to the specific type.
  72. Args:
  73. row (list[str]): One row data from parsed data.
  74. Returns:
  75. list[Union[str, float]], the converted data.
  76. """
  77. return [row[0], float(row[1]), int(row[2]), float(row[3])]
  78. class AicoreDetailAnalyser(BaseAnalyser):
  79. """
  80. The analyser for analyzing all the AICORE operators.
  81. Args:
  82. profiling_dir (str): The directory where the parsed profiling files are
  83. located.
  84. device_id (str): The device ID.
  85. Raises:
  86. ProfilerPathErrorException: If the profiling dir is invalid.
  87. """
  88. _col_names = ['op_name', 'op_type', 'avg_execution_time', 'subgraph',
  89. 'full_op_name', 'op_info']
  90. _file_name_aicore_detail_time = 'aicore_intermediate_{}_detail.csv'
  91. _file_name_framework_info = 'framework_raw_{}.csv'
  92. def __init__(self, profiling_dir, device_id):
  93. super().__init__(profiling_dir, device_id)
  94. self._none_filter_condition_key = [
  95. 'is_display_detail', 'is_display_full_op_name'
  96. ]
  97. self._none_sort_col_names = ['op_info']
  98. def query_and_sort_by_op_type(self, filter_condition, op_type_order: list):
  99. """
  100. Query the AICORE operator detail information by `filter_condition`,
  101. and sort by `op_type_order` and execution time.
  102. Args:
  103. filter_condition (dict): The filter condition.
  104. op_type_order (list[str]): The name of the operator type in order.
  105. Returns:
  106. dict, The results are filtered and sorted.
  107. """
  108. if filter_condition is None:
  109. filter_condition = {}
  110. self._filter(filter_condition)
  111. type_detail_cache = {}
  112. for detail_info in self._result:
  113. op_type = detail_info[1]
  114. if op_type not in op_type_order:
  115. continue
  116. infos = type_detail_cache.get(op_type)
  117. if infos:
  118. infos.append(detail_info)
  119. else:
  120. type_detail_cache[op_type] = [detail_info]
  121. result = []
  122. for op_type in op_type_order:
  123. detail_infos = type_detail_cache.get(op_type)
  124. if detail_infos is None:
  125. continue
  126. detail_infos.sort(key=lambda item: item[2], reverse=True)
  127. result.extend(detail_infos)
  128. return {
  129. 'col_name': self._display_col_names,
  130. 'object': result
  131. }
  132. def _load(self):
  133. """Load data according to the parsed AICORE operator file."""
  134. op_detail_file_path = os.path.join(
  135. self._profiling_dir,
  136. self._file_name_aicore_detail_time.format(self._device_id)
  137. )
  138. framework_file_path = os.path.join(
  139. self._profiling_dir,
  140. self._file_name_framework_info.format(self._device_id)
  141. )
  142. op_detail_file_path = validate_and_normalize_path(
  143. op_detail_file_path, raise_key='Invalid aicore_detail file path.'
  144. )
  145. framework_file_path = validate_and_normalize_path(
  146. framework_file_path, raise_key='Invalid framework file path.'
  147. )
  148. if not os.path.isfile(op_detail_file_path):
  149. logger.warning('The file <%s> does not exist.', op_detail_file_path)
  150. return
  151. if not os.path.isfile(framework_file_path):
  152. logger.warning('The file <%s> does not exist.', framework_file_path)
  153. return
  154. framework_infos = dict()
  155. with open(framework_file_path, 'r') as file:
  156. csv_reader = csv.reader(file)
  157. _ = next(csv_reader)
  158. for info in csv_reader:
  159. framework_infos[info[3]] = self._convert_framework_field_type(
  160. info
  161. )
  162. with open(op_detail_file_path, 'r') as file:
  163. csv_reader = csv.reader(file)
  164. _ = next(csv_reader)
  165. for info in csv_reader:
  166. detail_info = self._get_op_detail_info(info, framework_infos)
  167. self._data.append(detail_info)
  168. del framework_infos
  169. def _filter(self, filter_condition):
  170. """
  171. Filter the profiling data according to the filter condition.
  172. Args:
  173. filter_condition (dict): The filter condition.
  174. """
  175. def _inner_filter(item: list):
  176. return self._default_filter(item, filter_condition)
  177. def _inner_map(item: list):
  178. inner_item = item[0:4]
  179. if is_display_full_op_name:
  180. inner_item.append(item[4])
  181. if is_display_detail:
  182. inner_item.append(item[5])
  183. return inner_item
  184. is_display_detail = filter_condition.get('is_display_detail', True)
  185. is_display_full_op_name = filter_condition.get(
  186. 'is_display_full_op_name', True
  187. )
  188. self._set_display_col_name(is_display_detail, is_display_full_op_name)
  189. if is_display_detail and is_display_full_op_name:
  190. self._result = list(filter(_inner_filter, self._data))
  191. else:
  192. self._result = list(
  193. map(_inner_map, filter(_inner_filter, self._data))
  194. )
  195. def _set_display_col_name(self, is_display_detail, is_display_full_op_name):
  196. """
  197. Set the display column name according to the filter condition.
  198. Args:
  199. is_display_detail (bool): Whether to display the detailed operator
  200. information.
  201. is_display_full_op_name (bool): Whether to display the operator full
  202. name.
  203. """
  204. self._display_col_names = self._col_names[0:4]
  205. if is_display_full_op_name:
  206. self._display_col_names.append(self._col_names[4])
  207. if is_display_detail:
  208. self._display_col_names.append(self._col_names[5])
  209. def _convert_framework_field_type(self, row):
  210. """
  211. Convert the field type of framework file to the specific type.
  212. Args:
  213. row (list[str]): One row data from parsed data.
  214. Returns:
  215. list[Union[str, float]], the converted data.
  216. """
  217. return [row[3], row[4], row[5], row[6],
  218. json.loads(row[7]) if row[7] else None]
  219. def _get_op_detail_info(self, row, framework_infos):
  220. """
  221. Get operator detail information.
  222. Args:
  223. row (list[str]): One row data from parsed operator file.
  224. framework_infos (dict): All framework information.
  225. Returns:
  226. list[Union[str, float]], the operator detail information in one row.
  227. """
  228. framework_info = framework_infos.get(row[0])
  229. return [framework_info[1], framework_info[2], float(row[1]),
  230. framework_info[3], framework_info[0], framework_info[4]]
  231. class AicpuTypeAnalyser(BaseAnalyser):
  232. """
  233. The analyser for analyzing all the AICPU operators.
  234. Args:
  235. profiling_dir (str): The directory where the parsed profiling files are
  236. located.
  237. device_id (str): The device ID.
  238. Raises:
  239. ProfilerPathErrorException: If the profiling dir is invalid.
  240. """
  241. _col_names = ['op_type', 'execution_time', 'execution_frequency', 'percent']
  242. _file_name_aicpu_time = 'aicpu_intermediate_{}.csv'
  243. def _load(self):
  244. """Load data according to the parsed AICPU operator file."""
  245. aicpu_file_path = os.path.join(
  246. self._profiling_dir,
  247. self._file_name_aicpu_time.format(self._device_id)
  248. )
  249. aicpu_file_path = validate_and_normalize_path(
  250. aicpu_file_path, raise_key='Invalid aicpu file path.'
  251. )
  252. if not os.path.isfile(aicpu_file_path):
  253. logger.warning('The file <%s> does not exist.', aicpu_file_path)
  254. return
  255. type_detail_cache = dict()
  256. with open(aicpu_file_path, 'r') as file:
  257. csv_reader = csv.reader(file)
  258. _ = next(csv_reader)
  259. for item in csv_reader:
  260. op_type = item[1]
  261. info = type_detail_cache.get(op_type)
  262. if info:
  263. info.append(item)
  264. else:
  265. type_detail_cache[op_type] = [item]
  266. type_temp_detail_cache = dict()
  267. total_avg_time = 0
  268. result = []
  269. for key, value in type_detail_cache.items():
  270. exec_frequency = len(value)
  271. total_time_index = 2
  272. exec_avg_time = sum([float(i[total_time_index]) for i in value])/exec_frequency
  273. exec_avg_time = round(exec_avg_time, 6)
  274. total_avg_time += exec_avg_time
  275. type_temp_detail_cache[key] = [key, exec_avg_time, exec_frequency]
  276. for key, value in type_temp_detail_cache.items():
  277. execution_time_index = 1
  278. percent = round((value[execution_time_index]/total_avg_time)*100, 2)
  279. value.append(percent)
  280. result.append(value)
  281. self._data = result
  282. def _filter(self, filter_condition):
  283. """
  284. Filter the profiling data according to the filter condition.
  285. Args:
  286. filter_condition (dict): The filter condition.
  287. """
  288. def _inner_filter(item: list):
  289. return self._default_filter(item, filter_condition)
  290. self._result = list(filter(_inner_filter, self._data))
  291. class AicpuDetailAnalyser(BaseAnalyser):
  292. """
  293. The analyser for analyzing all the AICPU operators.
  294. Args:
  295. profiling_dir (str): The directory where the parsed profiling files are
  296. located.
  297. device_id (str): The device ID.
  298. Raises:
  299. ProfilerPathErrorException: If the profiling dir is invalid.
  300. """
  301. _col_names = ['serial_number', 'op_type', 'total_time', 'dispatch_time',
  302. 'run_start', 'run_end']
  303. _file_name_aicpu_time = 'aicpu_intermediate_{}.csv'
  304. def _load(self):
  305. """Load data according to the parsed AICPU operator file."""
  306. aicpu_file_path = os.path.join(
  307. self._profiling_dir,
  308. self._file_name_aicpu_time.format(self._device_id)
  309. )
  310. aicpu_file_path = validate_and_normalize_path(
  311. aicpu_file_path, raise_key='Invalid aicpu file path.'
  312. )
  313. if not os.path.isfile(aicpu_file_path):
  314. logger.warning('The file <%s> does not exist.', aicpu_file_path)
  315. return
  316. with open(aicpu_file_path, 'r') as file:
  317. csv_reader = csv.reader(file)
  318. _ = next(csv_reader)
  319. for info in csv_reader:
  320. aicpu_info = self._convert_field_type(info)
  321. self._data.append(aicpu_info)
  322. def _filter(self, filter_condition):
  323. """
  324. Filter the profiling data according to the filter condition.
  325. Args:
  326. filter_condition (dict): The filter condition.
  327. """
  328. def _inner_filter(item: list):
  329. return self._default_filter(item, filter_condition)
  330. self._result = list(filter(_inner_filter, self._data))
  331. def _convert_field_type(self, row):
  332. """
  333. Convert the field type to the specific type.
  334. Args:
  335. row (list[str]): One row data from parsed data.
  336. Returns:
  337. list[Union[str, float]], the converted data.
  338. """
  339. return [int(row[0]), row[1], float(row[2]), float(row[3]), int(row[4]),
  340. int(row[5])]