• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright 2023 Huawei Technologies Co., Ltd
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "pipeline/jit/pi/utils/utils.h"
17 #include <unordered_set>
18 #include <iomanip>
19 #include "ir/tensor.h"
20 #include "ir/map_tensor.h"
21 #include "pipeline/jit/pi/pydef.h"
22 #include "pybind11/pybind11.h"
23 #include "utils/log_adapter.h"
24 #include "pipeline/jit/pi/utils/opcode_util.h"
25 #include "runtime/hardware/device_context_manager.h"
26 
27 namespace mindspore {
28 namespace pijit {
29 
30 namespace py = pybind11;
31 
32 static const char kMutableAttr[] = "__ms_mutable__";
33 static const char kConstArgAttr[] = "const_arg";
34 static const char kDynamicLengthAttr[] = "__ms_dynamic_len__";
35 static const char kMsClassAttr[] = "__ms_class__";
36 static const int DefaultPercision = 6;
37 
GetStopTraceReasonDesc(StopTraceReason res)38 std::string GetStopTraceReasonDesc(StopTraceReason res) {
39 #define STOP_TRACE_REASON_KIND(kind, description) \
40   if (res == k##kind) {                           \
41     return description;                           \
42   }
43 #include "stop_trace_reason.def"
44 #undef STOP_TRACE_REASON_KIND
45   MS_EXCEPTION_IF_CHECK_FAIL(false, "Undefined STOP_TRACE_REASON");
46   return "";
47 }
48 
GetInlineReasonDesc(InlineReason res)49 std::string GetInlineReasonDesc(InlineReason res) {
50 #define INLINE_REASON_KIND(kind, description) \
51   if (res == k##kind) {                       \
52     return description;                       \
53   }
54 #include "inline_reason.def"
55 #undef INLINE_REASON_KIND
56   MS_EXCEPTION_IF_CHECK_FAIL(false, "Undefined INLINE_REASON");
57   return "";
58 }
59 
GetLoopUnrollingReasonDesc(LoopUnrollingReason res)60 std::string GetLoopUnrollingReasonDesc(LoopUnrollingReason res) {
61 #define LOOP_UNROLLING_REASON_KIND(kind, description) \
62   if (res == k##kind) {                               \
63     return description;                               \
64   }
65 #include "loop_unrolling_reason.def"
66 #undef LOOP_UNROLLING_REASON_KIND
67   MS_EXCEPTION_IF_CHECK_FAIL(false, "Undefined LOOP_UNROLLING_REASON");
68   return "";
69 }
70 
GetPyName(PyObject * obj)71 std::string Utils::GetPyName(PyObject *obj) {
72   const char *str = PyUnicode_AsUTF8(obj);
73   return str != nullptr ? std::string(str) : "";
74 }
75 
PyBuiltinPrint(PyObject * str)76 void Utils::PyBuiltinPrint(PyObject *str) {
77   static _PyCFunctionFastWithKeywords pyprint = nullptr;
78   if (!pyprint) {
79     PyObject *p = PyDict_GetItemString(PyEval_GetBuiltins(), "print");
80     MS_EXCEPTION_IF_CHECK_FAIL(p && PyCFunction_Check(p), "can't get python 'print' function");
81     pyprint = (_PyCFunctionFastWithKeywords)PyCFunction_GET_FUNCTION(p);
82   }
83   pyprint(nullptr, &str, 1, nullptr);
84   if (PyErr_Occurred()) {
85     PyErr_Print();
86     PyErr_Clear();
87   }
88 }
89 
DisFuncObject(PyObject * func)90 void Utils::DisFuncObject(PyObject *func) {
91   if (func == nullptr) {
92     GRAPH_JIT_LOG_F("(nil)\n");
93     return;
94   }
95   auto dis = py::module::import("dis").attr("dis");
96   // py::print("*** Dump ByteCode After CodeGen on [", py::cast<py::object>(func), "] ***");
97   PY_PRINT_F("*** Dump ByteCode After CodeGen on [%A] ***", func);
98   auto args = PyTuple_Pack(1, func);
99   Py_XDECREF(PyObject_Call(dis.ptr(), args, NULL));
100   Py_DECREF(args);
101   if (PyErr_Occurred()) {
102     PyErr_Print();
103   }
104 }
105 
GetModuleAttr(const std::string & mod_name,const std::string & attr_name,bool _import,bool _throw)106 py::object Utils::GetModuleAttr(const std::string &mod_name, const std::string &attr_name, bool _import, bool _throw) {
107   PyObject *name = PyUnicode_FromString(mod_name.c_str());
108   PyObject *mod = _import ? PyImport_Import(name) : PyImport_GetModule(name);
109   PyObject *attr = nullptr;
110   Py_DECREF(name);
111   if (mod != nullptr) {
112     attr = PyObject_GetAttrString(mod, attr_name.c_str());
113     Py_DECREF(mod);
114   }
115   if (attr != nullptr) {
116     return py::reinterpret_steal<py::object>(attr);
117   }
118   if (!_throw) {
119     PyErr_Clear();
120     return py::object();
121   }
122   if (!PyErr_Occurred()) {
123     if (mod == nullptr) {
124       if (_import) {
125         PyErr_Format(PyExc_ModuleNotFoundError, "No module named %s", mod_name.c_str());
126       } else {
127         PyErr_Format(PyExc_KeyError, "sys.modules[%s]", mod_name.c_str());
128       }
129     } else if (attr == nullptr) {
130       PyErr_Format(PyExc_AttributeError, "%S no attribute %s", mod, attr_name.c_str());
131     }
132   }
133   throw py::error_already_set();
134 }
135 
ReportPythonException()136 std::string Utils::ReportPythonException() {
137   if (PyErr_Occurred()) {
138     PyObject *et;
139     PyObject *ev;
140     PyObject *tb;
141     PyErr_Fetch(&et, &ev, &tb);
142     py::object s = py::str(ev);
143     MS_LOG(DEBUG) << "has python exception " << PyUnicode_AsUTF8(s.ptr());
144     return PyUnicode_AsUTF8(s.ptr());
145   }
146   return std::string();
147 }
148 
PackExArgs(const std::vector<py::object> & args,bool ret_vector_args)149 static std::pair<py::object, py::object> PackExArgs(const std::vector<py::object> &args, bool ret_vector_args) {
150   std::pair<py::object, py::object> failed;
151   py::object pargs;
152   py::object kwargs;
153   const int args_size = args.size();
154   do {
155     if (args_size == 0) {
156       return failed;
157     }
158     if (PyTuple_Check(args[0].ptr())) {
159       pargs = args[0];
160     } else {
161       pargs = py::reinterpret_steal<py::object>(PySequence_Tuple(args[0].ptr()));
162       PyErr_Clear();
163     }
164     if (args_size == 1 || pargs.ptr() == nullptr) {
165       break;
166     }
167     if (PyDict_Check(args[1].ptr())) {
168       kwargs = args[1];
169     } else {
170       kwargs = py::dict();
171       PyDict_Update(kwargs.ptr(), args[1].ptr());
172     }
173     if (PyErr_Occurred()) {
174       PyErr_Clear();
175       return failed;
176     }
177     if (ret_vector_args) {
178       PyObject *vals = PyDict_Values(kwargs.ptr());
179       PyObject *keys = PyDict_Keys(kwargs.ptr());
180       PyObject *new_args = PySequence_Concat(pargs.ptr(), vals);
181       Py_DECREF(vals);
182       pargs = py::reinterpret_steal<py::tuple>(new_args);
183       kwargs = py::reinterpret_steal<py::tuple>(keys);
184     }
185   } while (0);
186   return {pargs, kwargs};
187 }
188 
PackCallStackArgs(const std::vector<py::object> & args,int callop,bool ret_vector_args)189 std::pair<py::object, py::object> Utils::PackCallStackArgs(const std::vector<py::object> &args, int callop,
190                                                            bool ret_vector_args) {
191   std::pair<py::object, py::object> failed;
192   size_t args_size = args.size();
193   if (std::find_if(args.begin(), args.end(), [](const py::object &o) { return o.ptr() == nullptr; }) != args.end()) {
194     return failed;
195   }
196   Py_ssize_t psize = args_size;
197   Py_ssize_t kwsize = 0;
198   py::object pargs;
199   py::object kwargs;
200   if (callop == CALL_FUNCTION_KW || callop == CALL_FUNCTION || callop == CALL_METHOD) {
201     if (callop == CALL_FUNCTION_KW) {
202       py::object keys = args.back();
203       if (!PyTuple_Check(keys.ptr())) {
204         return failed;
205       }
206       psize--;
207       kwsize = PyTuple_GET_SIZE(keys.ptr());
208       if (psize < kwsize) {
209         return failed;
210       }
211       if (ret_vector_args) {
212         kwargs = keys;
213       } else {
214         psize -= kwsize;
215         kwargs = kwsize ? py::dict() : py::object();
216         for (auto i = 0; i < kwsize; ++i) {
217           PyDict_SetItem(kwargs.ptr(), PyTuple_GET_ITEM(keys.ptr(), i), args[psize + i].ptr());
218         }
219       }
220     }
221     pargs = py::tuple(psize);
222     for (Py_ssize_t i = 0; i < psize; ++i) {
223       PyTuple_SET_ITEM(pargs.ptr(), i, args[i].inc_ref().ptr());
224     }
225   } else if (callop == CALL_FUNCTION_EX) {
226     return PackExArgs(args, ret_vector_args);
227   } else {
228     return failed;
229   }
230   return {pargs, kwargs};
231 }
232 
233 #if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION < 9)
RetFrame(PyFrameObject * f,int)234 PyObject *RetFrame(PyFrameObject *f, int) {
235   Py_INCREF(f);
236   return reinterpret_cast<PyObject *>(f);
237 }
238 #else
RetFrame(PyThreadState *,PyFrameObject * f,int)239 PyObject *RetFrame(PyThreadState *, PyFrameObject *f, int) {
240   Py_INCREF(f);
241   return reinterpret_cast<PyObject *>(f);
242 }
243 #endif
244 
PrepareFrame(PyObject * callable,PyObject * args,PyObject * kwargs)245 PyFrameObject *Utils::PrepareFrame(PyObject *callable, PyObject *args, PyObject *kwargs) {
246   if (callable == nullptr || args == nullptr) {
247     return nullptr;
248   }
249   PyInterpreterState *inter = PyInterpreterState_Main();
250   _PyFrameEvalFunction prev = _PyInterpreterState_GetEvalFrameFunc(inter);
251   _PyInterpreterState_SetEvalFrameFunc(inter, RetFrame);
252   PyObject *frame = PyObject_Call(callable, args, kwargs);
253   _PyInterpreterState_SetEvalFrameFunc(inter, prev);
254   if (frame != nullptr) {
255     return reinterpret_cast<PyFrameObject *>(frame);
256   }
257   MS_LOG(DEBUG) << "prepare frame for " << std::string(py::str(callable)) << " failed because "
258                 << Utils::ReportPythonException();
259   return nullptr;
260 }
261 
MixedPrecisionTypeToDType(MixedPrecisionType mixed_type)262 PyObject *Utils::MixedPrecisionTypeToDType(MixedPrecisionType mixed_type) {
263   auto ms_dtype_obj = Utils::GetModuleAttr("mindspore", "dtype");
264   auto dtype_fp16_obj = ms_dtype_obj.attr("float16").ptr();
265   auto dtype_fp32_obj = ms_dtype_obj.attr("float32").ptr();
266   auto dtype_bf16_obj = ms_dtype_obj.attr("bfloat16").ptr();
267   auto dst_dtype = dtype_fp16_obj;
268   if (mixed_type == MixedPrecisionType::kFP32) {
269     dst_dtype = dtype_fp32_obj;
270   } else if (mixed_type == MixedPrecisionType::kBF16) {
271     dst_dtype = dtype_bf16_obj;
272   }
273   return dst_dtype;
274 }
275 
HasMutableOrConstAttr(PyObject * obj)276 bool HasMutableOrConstAttr(PyObject *obj) {
277   auto pyObj = py::cast<py::object>(obj);
278   return py::hasattr(pyObj, kMutableAttr) || py::hasattr(pyObj, kConstArgAttr);
279 }
280 
IsMutableObj(const py::object & obj)281 bool IsMutableObj(const py::object &obj) { return py::getattr(obj, kMutableAttr, nullptr).ptr() == Py_True; }
282 
CheckMutableOrNonConstAttr(PyObject * obj)283 bool CheckMutableOrNonConstAttr(PyObject *obj) {
284   auto pyObj = py::cast<py::object>(obj);
285   if (py::hasattr(pyObj, kMutableAttr)) {
286     return py::cast<bool>(py::getattr(pyObj, kMutableAttr));
287   } else if (py::hasattr(pyObj, kConstArgAttr)) {
288     return !py::cast<bool>(py::getattr(pyObj, kConstArgAttr));
289   } else {
290     return false;
291   }
292 }
293 
HasDynamicLength(PyObject * obj)294 bool HasDynamicLength(PyObject *obj) {
295   auto pyObj = py::cast<py::object>(obj);
296   return py::hasattr(pyObj, kDynamicLengthAttr);
297 }
298 
CheckDynamicLength(PyObject * obj)299 bool CheckDynamicLength(PyObject *obj) {
300   auto pyObj = py::cast<py::object>(obj);
301   if (py::hasattr(pyObj, kDynamicLengthAttr) && py::cast<bool>(py::getattr(pyObj, kDynamicLengthAttr))) {
302     return true;
303   } else {
304     return false;
305   }
306 }
307 
CheckScalar(PyObject * obj)308 bool CheckScalar(PyObject *obj) {
309   return PyLong_CheckExact(obj) || PyFloat_CheckExact(obj) || PyBool_Check(obj) || PyUnicode_CheckExact(obj) ||
310          PyBytes_CheckExact(obj) || PyComplex_CheckExact(obj);
311 }
312 
CheckContainer(PyObject * obj)313 bool CheckContainer(PyObject *obj) {
314   return PyList_CheckExact(obj) || PyTuple_CheckExact(obj) || PyAnySet_Check(obj) || PyDict_CheckExact(obj);
315 }
316 
IsTensorPyObject(PyObject * obj)317 bool IsTensorPyObject(PyObject *obj) {
318   return py::isinstance<mindspore::tensor::MapTensor>(obj) || py::isinstance<mindspore::tensor::Tensor>(obj) ||
319          py::isinstance<mindspore::tensor::MetaTensor>(obj) || py::isinstance<mindspore::tensor::CSRTensor>(obj) ||
320          py::isinstance<mindspore::tensor::RowTensor>(obj) || py::isinstance<mindspore::tensor::COOTensor>(obj) ||
321          py::isinstance<mindspore::tensor::TensorData>(obj);
322 }
323 
IsMsClass(PyObject * obj)324 bool IsMsClass(PyObject *obj) {
325   if (obj == nullptr) {
326     return false;
327   }
328   auto py_obj = py::cast<py::object>(obj);
329   return py::hasattr(py_obj, kMsClassAttr) && py::cast<bool>(py::getattr(py_obj, kMsClassAttr));
330 }
331 
IsNumpyObject(PyObject * op)332 bool IsNumpyObject(PyObject *op) {
333   if (op == nullptr) {
334     return false;
335   }
336   PyTypeObject *tp = Py_TYPE(op);
337   constexpr const char numpy[] = "numpy";
338   return tp->tp_name ? strncmp(tp->tp_name, numpy, sizeof(numpy) - 1) == 0 : false;
339 }
340 
GetTopModule(const py::object & o)341 std::string GetTopModule(const py::object &o) {
342   PyObject *mod = PyObject_GetAttrString(o.ptr(), "__module__");
343   const char *module_name = "";
344   if (mod == nullptr) {
345     PyErr_Clear();
346   } else if (PyModule_Check(mod)) {
347     module_name = PyModule_GetName(mod);
348   } else if (PyUnicode_Check(mod)) {
349     module_name = PyUnicode_AsUTF8(mod);
350   }
351   const char *s = strchr(module_name, '.');
352   std::string res = s ? std::string(module_name, s - module_name) : module_name;
353   Py_XDECREF(mod);
354   return res;
355 }
356 
GetPyCodeObject(const py::object & any,bool exact_func)357 py::object GetPyCodeObject(const py::object &any, bool exact_func) {
358   PyObject *f = any.ptr();
359   if (f == nullptr) {
360     return py::object();
361   }
362   if (PyInstanceMethod_Check(f)) {
363     f = PyInstanceMethod_GET_FUNCTION(f);
364   }
365   if (PyMethod_Check(f)) {
366     f = PyMethod_GET_FUNCTION(f);
367   }
368   if (PyFunction_Check(f)) {
369     f = PyFunction_GET_CODE(f);
370   }
371   if (PyCode_Check(f)) {
372     return py::cast<py::object>(f);
373   }
374   if (exact_func) {
375     return py::object();
376   }
377   PyObject *call = PyObject_GetAttrString(any.ptr(), "__call__");
378   if (call == nullptr) {
379     PyErr_Clear();
380     return py::object();
381   }
382   // self reference self at __call__, recursive call self.
383   // just call once
384   return GetPyCodeObject(py::reinterpret_steal<py::object>(call), true);
385 }
386 
GetFuncName(const py::object & f)387 const char *GetFuncName(const py::object &f) {
388   PyObject *func = f.ptr();
389   if (func == nullptr) {
390     return "";
391   }
392   if (PyMethod_Check(func)) {
393     func = PyMethod_GET_FUNCTION(func);
394   }
395   if (PyCFunction_Check(func)) {
396     return reinterpret_cast<PyCFunctionObject *>(func)->m_ml->ml_name;
397   }
398   PyCodeObject *co = nullptr;
399   if (PyFunction_Check(func)) {
400     co = reinterpret_cast<PyCodeObject *>(PyFunction_GET_CODE(func));
401   }
402   if (co) {
403     return PyUnicode_AsUTF8(co->co_name);
404   }
405   PyTypeObject *tp = PyType_Check(func) ? reinterpret_cast<PyTypeObject *>(func) : Py_TYPE(func);
406   const char *res = strrchr(tp->tp_name, '.');
407   return res ? res + 1 : tp->tp_name;
408 }
409 
CheckConstPyObject(PyObject * cnst)410 bool CheckConstPyObject(PyObject *cnst) {
411   static const std::unordered_set<PyTypeObject *> cnst_types = {
412     Py_TYPE(Py_None), Py_TYPE(Py_Ellipsis), Py_TYPE(Py_True), &PyCode_Type,  &PyFloat_Type,
413     &PyLong_Type,     &PyUnicode_Type,      &PyComplex_Type,  &PyBytes_Type,
414   };
415   if (cnst == nullptr) {
416     return false;
417   }
418   if (PyTuple_CheckExact(cnst)) {
419     // tuple can't reference self
420     PyObject **begin = &PyTuple_GET_ITEM(cnst, 0);
421     PyObject **end = begin + PyTuple_GET_SIZE(cnst);
422     return end == std::find_if_not(begin, end, CheckConstPyObject);
423   }
424   return cnst_types.find(Py_TYPE(cnst)) != cnst_types.end();
425 }
426 
GetAdapterTensorType()427 static py::object GetAdapterTensorType() {
428   py::object registry = Utils::GetModuleAttr("mindspore.common._register_for_adapter", "ms_adapter_registry");
429   return registry.ptr() == nullptr ? py::object() : py::getattr(registry, "tensor", nullptr);
430 }
431 
CheckAdapterTensor(const py::object & tensor)432 bool CheckAdapterTensor(const py::object &tensor) {
433   PyTypeObject *tp = reinterpret_cast<PyTypeObject *>(GetAdapterTensorType().ptr());
434   return Py_TYPE(tensor.ptr()) == tp;
435 }
436 
ConvertToAdapterTensor(const py::object & tensor)437 py::object ConvertToAdapterTensor(const py::object &tensor) {
438   py::object adapter_tensor_type = GetAdapterTensorType();
439   PyTypeObject *tp = reinterpret_cast<PyTypeObject *>(adapter_tensor_type.ptr());
440   if (Py_TYPE(tensor.ptr()) == tp) {
441     return tensor;
442   }
443   MS_EXCEPTION_IF_NULL(adapter_tensor_type.ptr());
444   PyObject *args[] = {tensor.ptr(), Py_True, nullptr};
445   py::tuple kw(1);
446   kw[0] = py::str("cast_tensor");
447   PyObject *adapter_tensor = PyObject_Vectorcall(adapter_tensor_type.ptr(), args, 1, kw.ptr());
448   if (!PyErr_Occurred()) {
449     return py::reinterpret_steal<py::object>(adapter_tensor);
450   }
451   throw py::error_already_set();
452 }
453 
ConvertToMsTensor(const py::object & tensor)454 py::object ConvertToMsTensor(const py::object &tensor) {
455   py::object common_tensor_type = Utils::GetModuleAttr("mindspore", "Tensor", false, true);
456   PyTypeObject *tp = reinterpret_cast<PyTypeObject *>(common_tensor_type.ptr());
457   return Py_TYPE(tensor.ptr()) == tp ? tensor : common_tensor_type(tensor);
458 }
459 
DeviceAvailableMemSize()460 size_t DeviceAvailableMemSize() {
461   const auto &context = MsContext::GetInstance();
462   uint32_t device_id = context->get_param<uint32_t>(MS_CTX_DEVICE_ID);
463   const std::string &device_target = context->get_param<std::string>(MS_CTX_DEVICE_TARGET);
464   const auto &m = device::DeviceContextManager::GetInstance().GetOrCreateDeviceContext({device_target, device_id});
465   MS_EXCEPTION_IF_NULL(m);
466   MS_EXCEPTION_IF_NULL(m->device_res_manager_);
467   return m->device_res_manager_->GetAvailableMemSize();
468 }
469 
GetInstance()470 RefTracker *RefTracker::GetInstance() {
471   static RefTracker instance;
472   return &instance;
473 }
474 
UnTrack(PyObject * self,PyObject * ref)475 PyObject *RefTracker::UnTrack(PyObject *self, PyObject *ref) {
476   auto &refs = RefTracker::GetInstance()->tracked_;
477   void *ptr = PyLong_AsVoidPtr(self);
478   auto iter = refs.find(ptr);
479   assert(iter != refs.end());
480   Py_DECREF(iter->second.first);
481   Py_DECREF(iter->second.second);
482   refs.erase(iter);
483   Py_RETURN_NONE;
484 }
485 
Track(PyObject * obj,const std::string & descr)486 bool RefTracker::Track(PyObject *obj, const std::string &descr) {
487   if (obj == nullptr) {
488     return false;
489   }
490   if (tracked_.find(obj) != tracked_.end()) {
491     return true;
492   }
493   PyObject *self = PyLong_FromVoidPtr(obj);
494   PyObject *callback = PyCFunction_New(&mdef_, self);
495   PyObject *ref = PyWeakref_NewRef(obj, callback);
496   Py_DECREF(callback);
497   Py_DECREF(self);
498   if (ref == nullptr) {
499     PyErr_Clear();
500     return false;
501   }
502   tracked_.insert({obj, {ref, PyUnicode_FromStringAndSize(descr.data(), descr.size())}});
503   return true;
504 }
505 
~RefTracker()506 RefTracker::~RefTracker() {
507   if (tracked_.empty()) {
508     return;
509   }
510   std::cout << "ref tracker not empty" << std::endl;
511   for (const auto &i : tracked_) {
512     PyObject *obj = PyWeakref_GET_OBJECT(i.second.first);
513     const char *descr = PyUnicode_AsUTF8(i.second.second);
514     assert(obj == i.first && obj != nullptr);
515     std::cout << "object " << (Py_TYPE(obj)->tp_name ? Py_TYPE(obj)->tp_name : "<unnamed>") << " at " << obj
516               << " refcnt " << Py_REFCNT(obj) << " descr " << descr << std::endl;
517   }
518 }
519 
RefTracker()520 RefTracker::RefTracker() : mdef_({"ref_untrack", &RefTracker::UnTrack, METH_O, PyDoc_STR("")}) {}
521 
TimeRecorder(const RecorderType & descr,bool record)522 TimeRecorder::TimeRecorder(const RecorderType &descr, bool record) : descr_(descr), record_(record) {
523   if (record_) {
524     start_ = std::chrono::steady_clock::now();
525   }
526 }
527 
~TimeRecorder()528 TimeRecorder::~TimeRecorder() {
529   if (record_) {
530     uint64_t clk = static_cast<uint64_t>((std::chrono::steady_clock::now() - start_).count());
531     auto &data = TimeRecorder::Data()->data_[descr_];
532     data.count++;
533     data.nano += clk;
534   }
535 }
536 
Data()537 TimeRecorder::TimeData *TimeRecorder::Data() {
538   static TimeData data;
539   return &data;
540 }
541 
~TimeData()542 TimeRecorder::TimeData::~TimeData() {
543   if (!data_.empty()) {
544     std::cout << ToString() << std::endl;
545   }
546 }
547 
ToString()548 std::string TimeRecorder::TimeData::ToString() {
549   if (data_.empty()) {
550     return std::string();
551   }
552 
553   const auto Fmt = [](std::ostream &s) -> std::ostream & {
554     const int w = 20;
555     s << std::setw(w) << std::left;
556     return s;
557   };
558 
559   std::stringstream s;
560   s.precision(DefaultPercision);
561   s.setf(std::ios::fixed);
562   s << "============= TimeRecorder =============" << std::endl;
563   s << Fmt << "type" << Fmt << "times" << Fmt << "seconds" << std::endl;
564   for (const auto &i : data_) {
565     s << Fmt << i.first << Fmt << i.second.count << Fmt << (i.second.nano / TimeRecorder::scale) << std::endl;
566   }
567   s << "========================================" << std::endl;
568   return s.str();
569 }
570 
571 }  // namespace pijit
572 }  // namespace mindspore
573