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.0 kB

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

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