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.

plugin.py 8.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. # -*- coding: utf-8 -*-
  2. # MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
  3. #
  4. # Copyright (c) 2014-2020 Megvii Inc. All rights reserved.
  5. #
  6. # Unless required by applicable law or agreed to in writing,
  7. # software distributed under the License is distributed on an
  8. # "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. """plugins associated with computing graph"""
  10. import atexit
  11. import collections
  12. import json
  13. import os
  14. import platform
  15. import signal
  16. import struct
  17. import numpy as np
  18. from . import mgb as _mgb
  19. from .logconf import get_logger
  20. InfkernFinderInputValueRec = collections.namedtuple(
  21. "InfkernFinderInputValueRec", ["var_name", "var_id", "run_id", "value"]
  22. )
  23. class CompGraphProfiler(_mgb._CompGraphProfilerImpl):
  24. """a plugin to profile computing graphs"""
  25. def __init__(self, comp_graph):
  26. super().__init__(comp_graph)
  27. def get(self):
  28. """get visualizable profiling result on a function"""
  29. return json.loads(self._get_result())
  30. def write_json(self, fobj):
  31. """write the result to a json file
  32. :param fobj: a file-like object, or a string
  33. """
  34. if isinstance(fobj, str):
  35. with open(fobj, "w") as fout:
  36. return self.write_json(fout)
  37. fobj.write(self._get_result())
  38. class NumRangeChecker(_mgb._NumRangeCheckerImpl):
  39. """check that all numberical float values of variables in a computing graph
  40. are within given range"""
  41. def __init__(self, comp_graph, max_abs_val):
  42. """:param max_abs_val: max absolute value"""
  43. super().__init__(comp_graph, float(max_abs_val))
  44. class TextOprIODump(_mgb._TextOprIODumpImpl):
  45. """dump all internal results as text to a file"""
  46. def __init__(self, comp_graph, fpath, *, print_addr=None, max_size=None):
  47. super().__init__(comp_graph, fpath)
  48. if print_addr is not None:
  49. self.print_addr(print_addr)
  50. if max_size is not None:
  51. self.max_size(max_size)
  52. def print_addr(self, flag):
  53. """set whether to print var address
  54. :return: self
  55. """
  56. self._print_addr(flag)
  57. return self
  58. def max_size(self, size):
  59. """set the number of elements to be printed for each var
  60. :return: self
  61. """
  62. self._max_size(size)
  63. return self
  64. class BinaryOprIODump(_mgb._BinaryOprIODumpImpl):
  65. """dump all internal results binary files to a directory; the values can be
  66. loaded by :func:`load_tensor_binary`
  67. """
  68. def __init__(self, comp_graph, dir_path):
  69. super().__init__(comp_graph, dir_path)
  70. class InfkernFinder(_mgb._InfkernFinderImpl):
  71. """a plugin to find kernels that cause infinite loops"""
  72. def __init__(self, comp_graph, record_input_value):
  73. """
  74. :param record_input_value: whether need to record input var values of
  75. all operators
  76. :type record_input_value: bool
  77. """
  78. super().__init__(comp_graph, record_input_value)
  79. def write_to_file(self, fpath):
  80. """write current execution status to a text file
  81. :return: ID of the first operator that is still not finished,
  82. or None if all oprs are finished
  83. :rtype: int or None
  84. """
  85. v = self._write_to_file(fpath)
  86. if v == 0:
  87. return
  88. return v - 1
  89. def get_input_values(self, opr_id):
  90. """get recorded input values of a given operator. Return a list
  91. of :class:`InfkernFinderInputValueRec`. Note that the value in
  92. each item is either None (if it is not recorded) or a numpy
  93. array
  94. """
  95. ret = []
  96. for idx in range(self._get_input_values_prepare(opr_id)):
  97. vn = self._get_input_values_var_name(idx)
  98. vi = self._get_input_values_var_idx(idx)
  99. ri = self._get_input_values_run_id(idx)
  100. val = self._get_input_values_val(idx)
  101. if not val.shape:
  102. val = None
  103. else:
  104. val = val.get_value()
  105. ret.append(InfkernFinderInputValueRec(vn, vi, ri, val))
  106. return ret
  107. def fast_signal_hander(signum, callback):
  108. """bypass python's signal handling system and registera handler that is
  109. called ASAP in a dedicated thread (in contrary, python calls handlers in
  110. the main thread)
  111. :param callback: signal callback, taking the signal number as its sole
  112. argument
  113. """
  114. def cb_wrapped():
  115. try:
  116. callback(signum)
  117. except:
  118. get_logger().exception("error calling signal handler for {}".format(signum))
  119. _mgb._FastSignal.register_handler(signum, cb_wrapped)
  120. atexit.register(_mgb._FastSignal.shutdown)
  121. class GlobalInfkernFinder:
  122. """
  123. manage a list of :class:`InfkernFinder` objects; when this process is
  124. signaled with SIGUSR1, an interactive IPython shell would be presented for
  125. further investigation
  126. """
  127. _signal = None
  128. if platform.system() != "Windows":
  129. _signal = signal.SIGUSR1
  130. else:
  131. _signal = signal.CTRL_C_EVENT
  132. _registry = []
  133. _shell_maker = None
  134. @classmethod
  135. def add_graph(cls, comp_graph):
  136. """register a graph so it can be tracked by :class:`InfkernFinder`"""
  137. enabled = os.getenv("MGB_DBG_INFKERN_FINDER")
  138. if not enabled:
  139. return
  140. if enabled == "1":
  141. record_input_value = False
  142. else:
  143. assert enabled == "2", (
  144. "MGB_DBG_INFKERN_FINDER must be either 1 or 2, indicating "
  145. "whether to record input values"
  146. )
  147. record_input_value = True
  148. finder = InfkernFinder(comp_graph, record_input_value)
  149. get_logger().warning(
  150. "interactive InfkernFinder {} registered to graph {}; all input "
  151. "var values would be recorded and the graph would never be "
  152. "reclaimed. You can enter the interactive debug session by "
  153. 'executing "kill -{} {}". record_input_value={}'.format(
  154. finder, comp_graph, cls._signal, os.getpid(), record_input_value
  155. )
  156. )
  157. if not cls._registry:
  158. from IPython.terminal.embed import InteractiveShellEmbed
  159. cls._shell_maker = InteractiveShellEmbed
  160. fast_signal_hander(cls._signal, cls._on_signal)
  161. cls._registry.append(finder)
  162. @classmethod
  163. def _on_signal(cls, signum):
  164. shell = cls._shell_maker()
  165. shell(
  166. header="Enter interactive InfkernFinder session; the registered "
  167. "finder objects can be found in variable f",
  168. local_ns={"f": cls._registry},
  169. )
  170. def load_tensor_binary(fobj):
  171. """load a tensor dumped by the :class:`BinaryOprIODump` plugin; the actual
  172. tensor value dump is implemented by ``mgb::debug::dump_tensor``.
  173. Multiple values can be compared by ``tools/compare_binary_iodump.py``.
  174. :param fobj: file object, or a string that contains the file name
  175. :return: tuple ``(tensor_value, tensor_name)``
  176. """
  177. if isinstance(fobj, str):
  178. with open(fobj, "rb") as fin:
  179. return load_tensor_binary(fin)
  180. DTYPE_LIST = {
  181. 0: np.float32,
  182. 1: np.uint8,
  183. 2: np.int8,
  184. 3: np.int16,
  185. 4: np.int32,
  186. 5: _mgb.intb1,
  187. 6: _mgb.intb2,
  188. 7: _mgb.intb4,
  189. 8: None,
  190. 9: np.float16,
  191. # quantized dtype start from 100000
  192. # see MEGDNN_PARAMETERIZED_DTYPE_ENUM_BASE in
  193. # dnn/include/megdnn/dtype.h
  194. 100000: np.uint8,
  195. 100001: np.int32,
  196. 100002: np.int8,
  197. }
  198. header_fmt = struct.Struct("III")
  199. name_len, dtype, max_ndim = header_fmt.unpack(fobj.read(header_fmt.size))
  200. assert (
  201. DTYPE_LIST[dtype] is not None
  202. ), "Cannot load this tensor: dtype Byte is unsupported."
  203. shape = list(struct.unpack("I" * max_ndim, fobj.read(max_ndim * 4)))
  204. while shape[-1] == 0:
  205. shape.pop(-1)
  206. name = fobj.read(name_len).decode("ascii")
  207. return np.fromfile(fobj, dtype=DTYPE_LIST[dtype]).reshape(shape), name

MegEngine 安装包中集成了使用 GPU 运行代码所需的 CUDA 环境,不用区分 CPU 和 GPU 版。 如果想要运行 GPU 程序,请确保机器本身配有 GPU 硬件设备并安装好驱动。 如果你想体验在云端 GPU 算力平台进行深度学习开发的感觉,欢迎访问 MegStudio 平台