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