未验证 提交 c15e3823 编写于 作者: C chenjian 提交者: GitHub

Add profiler features (#40357)

* add event record for model profiling

* fix format

* fix format

* fix code example bug

* no

* add profiler statistic

* add profiler feature

* fix bug

* fix bug

* fix bug

* fix bug

* required: gpu

* required: gpu

* fix bug

* required: gpu

* fix ci bug

* fix ci error

* fix ci error

* upgrade document

* fix doc

* fix ci bug

* add doc and fix bug

* nothing

* fix bug

* fix format bug

* modify format

* add deprecated description for old profiler

* fix bug

* fix bug

* fix

* add load_profiler_reuslt doc

* add load_profiler_reuslt doc

* add load_profiler_reuslt doc

* help fix old profiler sample code

* add api doc

* fix format

* fix api doc

* fix api doc format

* fix api doc format

* fix api doc c format

* fix api doc format
上级 58970995
...@@ -118,8 +118,9 @@ float CpuUtilization::GetCpuUtilization() { ...@@ -118,8 +118,9 @@ float CpuUtilization::GetCpuUtilization() {
float busy_time = (system_kernel_time_end - system_kernel_time_start) + float busy_time = (system_kernel_time_end - system_kernel_time_start) +
(system_user_time_end - system_user_time_start); (system_user_time_end - system_user_time_start);
float idle_time = system_idle_time_end - system_idle_time_start; float idle_time = system_idle_time_end - system_idle_time_start;
cpu_utilization = busy_time / (busy_time + idle_time); if (busy_time + idle_time != 0) {
cpu_utilization = busy_time / (busy_time + idle_time);
}
#elif defined(__linux__) #elif defined(__linux__)
float busy_time = (system_tms_end_.tms_utime - system_tms_start_.tms_utime) + float busy_time = (system_tms_end_.tms_utime - system_tms_start_.tms_utime) +
(system_tms_end_.tms_stime - system_tms_start_.tms_stime) + (system_tms_end_.tms_stime - system_tms_start_.tms_stime) +
...@@ -127,7 +128,9 @@ float CpuUtilization::GetCpuUtilization() { ...@@ -127,7 +128,9 @@ float CpuUtilization::GetCpuUtilization() {
(irq_end_ - irq_start_) + (softirq_end_ - softirq_start_) + (irq_end_ - irq_start_) + (softirq_end_ - softirq_start_) +
(steal_end_ - steal_start_); (steal_end_ - steal_start_);
float idle_time = (idle_end_ - idle_start_) + (iowait_end_ - iowait_start_); float idle_time = (idle_end_ - idle_start_) + (iowait_end_ - iowait_start_);
cpu_utilization = busy_time / (busy_time + idle_time); if (busy_time + idle_time != 0) {
cpu_utilization = busy_time / (busy_time + idle_time);
}
#else #else
LOG(WARNING) LOG(WARNING)
<< "Current System is not supported to get system cpu utilization" << "Current System is not supported to get system cpu utilization"
...@@ -148,13 +151,16 @@ float CpuUtilization::GetCpuCurProcessUtilization() { ...@@ -148,13 +151,16 @@ float CpuUtilization::GetCpuCurProcessUtilization() {
uint64_t end = FileTimeToUint64(end_); uint64_t end = FileTimeToUint64(end_);
float busy_time = (process_kernel_time_end - process_kernel_time_start) + float busy_time = (process_kernel_time_end - process_kernel_time_start) +
(process_user_time_end - process_user_time_start); (process_user_time_end - process_user_time_start);
cpu_process_utilization = busy_time / (end - start); if (end - start != 0) {
LOG(INFO) << "Process Utilization = " << cpu_process_utilization << std::endl; cpu_process_utilization = busy_time / (end - start);
}
#elif defined(__linux__) #elif defined(__linux__)
float busy_time = float busy_time =
(process_tms_end_.tms_utime - process_tms_start_.tms_utime) + (process_tms_end_.tms_utime - process_tms_start_.tms_utime) +
(process_tms_end_.tms_stime - process_tms_start_.tms_stime); (process_tms_end_.tms_stime - process_tms_start_.tms_stime);
cpu_process_utilization = busy_time / (end_ - start_); if (end_ - start_ != 0) {
cpu_process_utilization = busy_time / (end_ - start_);
}
#else #else
LOG(WARNING) LOG(WARNING)
<< "Current System is not supported to get process cpu utilization" << "Current System is not supported to get process cpu utilization"
......
...@@ -44,6 +44,14 @@ std::unique_ptr<Profiler> Profiler::Create(const ProfilerOptions& options) { ...@@ -44,6 +44,14 @@ std::unique_ptr<Profiler> Profiler::Create(const ProfilerOptions& options) {
return std::unique_ptr<Profiler>(new Profiler(options)); return std::unique_ptr<Profiler>(new Profiler(options));
} }
bool Profiler::IsCuptiSupported() {
bool supported = false;
#ifdef PADDLE_WITH_CUPTI
supported = true;
#endif
return supported;
}
Profiler::Profiler(const ProfilerOptions& options) { Profiler::Profiler(const ProfilerOptions& options) {
options_ = options; options_ = options;
std::bitset<32> trace_switch(options_.trace_switch); std::bitset<32> trace_switch(options_.trace_switch);
......
...@@ -43,6 +43,8 @@ class Profiler { ...@@ -43,6 +43,8 @@ class Profiler {
public: public:
static std::unique_ptr<Profiler> Create(const ProfilerOptions& options); static std::unique_ptr<Profiler> Create(const ProfilerOptions& options);
static bool IsCuptiSupported();
void Prepare(); void Prepare();
void Start(); void Start();
......
...@@ -18,7 +18,6 @@ limitations under the License. */ ...@@ -18,7 +18,6 @@ limitations under the License. */
#include "glog/logging.h" #include "glog/logging.h"
#include "paddle/fluid/platform/device/gpu/gpu_info.h" #include "paddle/fluid/platform/device/gpu/gpu_info.h"
#include "paddle/fluid/platform/dynload/cupti.h"
namespace paddle { namespace paddle {
namespace platform { namespace platform {
......
...@@ -15,6 +15,7 @@ limitations under the License. */ ...@@ -15,6 +15,7 @@ limitations under the License. */
#include <ctime> #include <ctime>
#include <string> #include <string>
#include "paddle/fluid/platform/dynload/cupti.h"
#include "paddle/fluid/platform/enforce.h" #include "paddle/fluid/platform/enforce.h"
#include "paddle/fluid/platform/os_info.h" #include "paddle/fluid/platform/os_info.h"
......
...@@ -3322,6 +3322,7 @@ All parameter, weight, gradient are variables in Paddle. ...@@ -3322,6 +3322,7 @@ All parameter, weight, gradient are variables in Paddle.
py::class_<paddle::platform::Profiler>(m, "_Profiler") py::class_<paddle::platform::Profiler>(m, "_Profiler")
.def("create", &paddle::platform::Profiler::Create, .def("create", &paddle::platform::Profiler::Create,
py::return_value_policy::take_ownership) py::return_value_policy::take_ownership)
.def("is_cupti_supported", &paddle::platform::Profiler::IsCuptiSupported)
.def("prepare", .def("prepare",
[](paddle::platform::Profiler *profiler) { [](paddle::platform::Profiler *profiler) {
platform::EnableHostEventRecorder(); platform::EnableHostEventRecorder();
......
...@@ -30,6 +30,7 @@ from paddle.fluid.framework import _set_expected_place, _current_expected_place, ...@@ -30,6 +30,7 @@ from paddle.fluid.framework import _set_expected_place, _current_expected_place,
import queue import queue
import paddle import paddle
import paddle.profiler as profiler
from .. import core, layers from .. import core, layers
from ..framework import in_dygraph_mode, _in_eager_mode from ..framework import in_dygraph_mode, _in_eager_mode
from ..multiprocess_utils import _set_SIGCHLD_handler, MP_STATUS_CHECK_INTERVAL, CleanupFuncRegistrar from ..multiprocess_utils import _set_SIGCHLD_handler, MP_STATUS_CHECK_INTERVAL, CleanupFuncRegistrar
...@@ -250,6 +251,10 @@ class _DataLoaderIterSingleProcess(_DataLoaderIterBase): ...@@ -250,6 +251,10 @@ class _DataLoaderIterSingleProcess(_DataLoaderIterBase):
self._exit_thread_expectedly() self._exit_thread_expectedly()
def __next__(self): def __next__(self):
trace_event = profiler.RecordEvent(
name="_DataLoaderIterSingleProcess",
event_type=profiler.TracerEventType.Dataloader)
trace_event.begin()
try: try:
if in_dygraph_mode(): if in_dygraph_mode():
if _in_eager_mode(): if _in_eager_mode():
...@@ -283,6 +288,8 @@ class _DataLoaderIterSingleProcess(_DataLoaderIterBase): ...@@ -283,6 +288,8 @@ class _DataLoaderIterSingleProcess(_DataLoaderIterBase):
self._reader.shutdown() self._reader.shutdown()
self._try_shutdown_all() self._try_shutdown_all()
six.reraise(*sys.exc_info()) six.reraise(*sys.exc_info())
finally:
trace_event.end()
def _shutdown_thread(self): def _shutdown_thread(self):
if self._thread: if self._thread:
...@@ -695,6 +702,10 @@ class _DataLoaderIterMultiProcess(_DataLoaderIterBase): ...@@ -695,6 +702,10 @@ class _DataLoaderIterMultiProcess(_DataLoaderIterBase):
self._try_shutdown_all(1) self._try_shutdown_all(1)
def __next__(self): def __next__(self):
trace_event = profiler.RecordEvent(
name="_DataLoaderIterMultiProcess",
event_type=profiler.TracerEventType.Dataloader)
trace_event.begin()
try: try:
# _batches_outstanding here record the total batch data number # _batches_outstanding here record the total batch data number
# in 'from after _try_put_indices to beforeoutput data', this # in 'from after _try_put_indices to beforeoutput data', this
...@@ -743,6 +754,8 @@ class _DataLoaderIterMultiProcess(_DataLoaderIterBase): ...@@ -743,6 +754,8 @@ class _DataLoaderIterMultiProcess(_DataLoaderIterBase):
self._reader.shutdown() self._reader.shutdown()
self._try_shutdown_all() self._try_shutdown_all()
six.reraise(*sys.exc_info()) six.reraise(*sys.exc_info())
finally:
trace_event.end()
# python2 compatibility # python2 compatibility
def next(self): def next(self):
......
...@@ -25,6 +25,7 @@ from copy import deepcopy ...@@ -25,6 +25,7 @@ from copy import deepcopy
import inspect import inspect
import paddle import paddle
import paddle.profiler as profiler
from . import parallel_helper from . import parallel_helper
from .. import unique_name from .. import unique_name
...@@ -905,7 +906,9 @@ class Layer(object): ...@@ -905,7 +906,9 @@ class Layer(object):
self._built = True self._built = True
outputs = self.forward(*inputs, **kwargs) with profiler.RecordEvent(self.full_name(),
profiler.TracerEventType.Forward):
outputs = self.forward(*inputs, **kwargs)
for forward_post_hook in self._forward_post_hooks.values(): for forward_post_hook in self._forward_post_hooks.values():
hook_result = forward_post_hook(self, inputs, outputs) hook_result = forward_post_hook(self, inputs, outputs)
......
...@@ -28,6 +28,7 @@ from .math_op_patch import monkey_patch_math_varbase ...@@ -28,6 +28,7 @@ from .math_op_patch import monkey_patch_math_varbase
from .parallel import scale_loss from .parallel import scale_loss
from paddle.fluid.data_feeder import convert_dtype, _PADDLE_DTYPE_2_NUMPY_DTYPE from paddle.fluid.data_feeder import convert_dtype, _PADDLE_DTYPE_2_NUMPY_DTYPE
import paddle.utils.deprecated as deprecated import paddle.utils.deprecated as deprecated
import paddle.profiler as profiler
from paddle import _C_ops from paddle import _C_ops
...@@ -199,8 +200,8 @@ def monkey_patch_varbase(): ...@@ -199,8 +200,8 @@ def monkey_patch_varbase():
You can clear gradient by ``Tensor.clear_grad()`` . You can clear gradient by ``Tensor.clear_grad()`` .
Args: Args:
grad_tensor(Tensor, optional): initial gradient values of the current Tensor. If `grad_tensor` is None, grad_tensor(Tensor, optional): initial gradient values of the current Tensor. If `grad_tensor` is None,
the initial gradient values of the current Tensor would be Tensor filled with 1.0; the initial gradient values of the current Tensor would be Tensor filled with 1.0;
if `grad_tensor` is not None, it must have the same length as the current Tensor. if `grad_tensor` is not None, it must have the same length as the current Tensor.
Teh default value is None. Teh default value is None.
...@@ -243,6 +244,9 @@ def monkey_patch_varbase(): ...@@ -243,6 +244,9 @@ def monkey_patch_varbase():
""" """
if framework.in_dygraph_mode(): if framework.in_dygraph_mode():
record_event = profiler.RecordEvent(
"Gradient Backward", profiler.TracerEventType.Backward)
record_event.begin()
if grad_tensor is not None: if grad_tensor is not None:
if core._in_eager_mode(): if core._in_eager_mode():
assert isinstance( assert isinstance(
...@@ -278,6 +282,7 @@ def monkey_patch_varbase(): ...@@ -278,6 +282,7 @@ def monkey_patch_varbase():
core.dygraph_run_backward([self], [grad_tensor], core.dygraph_run_backward([self], [grad_tensor],
retain_graph, retain_graph,
framework._dygraph_tracer()) framework._dygraph_tracer())
record_event.end()
else: else:
raise ValueError( raise ValueError(
"Variable.backward() is only available in DyGraph mode") "Variable.backward() is only available in DyGraph mode")
...@@ -476,7 +481,7 @@ def monkey_patch_varbase(): ...@@ -476,7 +481,7 @@ def monkey_patch_varbase():
def grad(self): def grad(self):
""" """
.. warning:: .. warning::
This API will return the tensor value of the gradient. If you want This API will return the tensor value of the gradient. If you want
to get the numpy value of the gradient, you can use :code:`x.grad.numpy()`. to get the numpy value of the gradient, you can use :code:`x.grad.numpy()`.
Get the Gradient of Current Tensor. Get the Gradient of Current Tensor.
...@@ -515,7 +520,7 @@ def monkey_patch_varbase(): ...@@ -515,7 +520,7 @@ def monkey_patch_varbase():
def item(self, *args): def item(self, *args):
""" """
Convert element at specific position in Tensor into Python scalars. If the position is not specified, the Tensor must be a Convert element at specific position in Tensor into Python scalars. If the position is not specified, the Tensor must be a
single-element Tensor. single-element Tensor.
Args: Args:
...@@ -526,7 +531,7 @@ def monkey_patch_varbase(): ...@@ -526,7 +531,7 @@ def monkey_patch_varbase():
Raises: Raises:
ValueError: If the Tensor has more than one element, there must be coordinates. ValueError: If the Tensor has more than one element, there must be coordinates.
Examples: Examples:
.. code-block:: python .. code-block:: python
...@@ -588,7 +593,7 @@ def monkey_patch_varbase(): ...@@ -588,7 +593,7 @@ def monkey_patch_varbase():
import paddle import paddle
x = paddle.rand([2, 5]) x = paddle.rand([2, 5])
print(x) print(x)
# Tensor(shape=[2, 5], dtype=float32, place=CPUPlace, # Tensor(shape=[2, 5], dtype=float32, place=CPUPlace,
# [[0.30574632, 0.55739117, 0.30902600, 0.39413780, 0.44830436], # [[0.30574632, 0.55739117, 0.30902600, 0.39413780, 0.44830436],
# [0.79010487, 0.53972793, 0.09495186, 0.44267157, 0.72112119]]) # [0.79010487, 0.53972793, 0.09495186, 0.44267157, 0.72112119]])
...@@ -611,7 +616,7 @@ def monkey_patch_varbase(): ...@@ -611,7 +616,7 @@ def monkey_patch_varbase():
import copy import copy
x = paddle.to_tensor(2.) x = paddle.to_tensor(2.)
y = copy.deepcopy(x) y = copy.deepcopy(x)
print(x) print(x)
# Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=True, # Tensor(shape=[1], dtype=float32, place=CPUPlace, stop_gradient=True,
# [2.]) # [2.])
...@@ -655,7 +660,7 @@ def monkey_patch_varbase(): ...@@ -655,7 +660,7 @@ def monkey_patch_varbase():
def __array__(self, dtype=None): def __array__(self, dtype=None):
""" """
Returns a numpy array shows the value of current Tensor. Returns a numpy array shows the value of current Tensor.
Returns: Returns:
ndarray: The numpy value of current Tensor. ndarray: The numpy value of current Tensor.
......
...@@ -20,6 +20,8 @@ import os ...@@ -20,6 +20,8 @@ import os
import six import six
import sys import sys
from paddle.utils.deprecated import deprecated
__all__ = [ __all__ = [
'cuda_profiler', 'reset_profiler', 'profiler', 'start_profiler', 'cuda_profiler', 'reset_profiler', 'profiler', 'start_profiler',
'stop_profiler' 'stop_profiler'
...@@ -36,10 +38,16 @@ NVPROF_CONFIG = [ ...@@ -36,10 +38,16 @@ NVPROF_CONFIG = [
] ]
@deprecated(
since="2.3.0",
update_to="paddle.profiler.Profiler",
level=1,
reason="Please use new profiler tool, this profiler tool is no longer maintained."
)
@signature_safe_contextmanager @signature_safe_contextmanager
def cuda_profiler(output_file, output_mode=None, config=None): def cuda_profiler(output_file, output_mode=None, config=None):
""" """
API cuda_profiler has been abandoned. If you have relevant requirements, you can use `paddle.utils.profiler.start_profiler` and `paddle.utils.profiler.stop_profiler`. API cuda_profiler has been abandoned. If you have relevant requirements, you can use `paddle.utils.profiler.start_profiler` and `paddle.utils.profiler.stop_profiler`.
The relevant reference documents are as follows: The relevant reference documents are as follows:
<https://www.paddlepaddle.org.cn/documentation/docs/en/api/paddle/utils/profiler/start_profiler_en.html#start-profiler> <https://www.paddlepaddle.org.cn/documentation/docs/en/api/paddle/utils/profiler/start_profiler_en.html#start-profiler>
<https://www.paddlepaddle.org.cn/documentation/docs/en/api/paddle/utils/profiler/stop_profiler_en.html#stop-profiler> <https://www.paddlepaddle.org.cn/documentation/docs/en/api/paddle/utils/profiler/stop_profiler_en.html#stop-profiler>
...@@ -54,18 +62,18 @@ def cuda_profiler(output_file, output_mode=None, config=None): ...@@ -54,18 +62,18 @@ def cuda_profiler(output_file, output_mode=None, config=None):
def npu_profiler(output_file, config=None): def npu_profiler(output_file, config=None):
""" """
The NPU profiler. The NPU profiler.
This fuctions is used to profile NPU program by NPU runtime application This fuctions is used to profile NPU program by NPU runtime application
programming interface. The profiling result will be written into programming interface. The profiling result will be written into
`output_file`. The users can set set the NPU profiling config by `config` argument. `output_file`. The users can set set the NPU profiling config by `config` argument.
After getting the profiling result file, users can use After getting the profiling result file, users can use
`tools provided by Ascend <https://support.huaweicloud.com/tg-Inference-cann/atlasprofiling_16_0006.html>`_ `tools provided by Ascend <https://support.huaweicloud.com/tg-Inference-cann/atlasprofiling_16_0006.html>`_
to load this output file to visualize results. to load this output file to visualize results.
Args: Args:
output_file (str) : The output file name, the result will be output_file (str) : The output file name, the result will be
written into this file. It should be absolute path. written into this file. It should be absolute path.
config (list<str>, optional) : NPU profile config. For more details, please config (list<str>, optional) : NPU profile config. For more details, please
refer to `User Guide <https://support.huaweicloud.com/tg-Inference-cann/atlasprofiling_16_0006.html>`_ . refer to `User Guide <https://support.huaweicloud.com/tg-Inference-cann/atlasprofiling_16_0006.html>`_ .
...@@ -109,6 +117,12 @@ def npu_profiler(output_file, config=None): ...@@ -109,6 +117,12 @@ def npu_profiler(output_file, config=None):
core.npu_prof_finalize() core.npu_prof_finalize()
@deprecated(
since="2.3.0",
update_to="paddle.profiler.Profiler",
level=1,
reason="Please use new profiler tool, this profiler tool is no longer maintained."
)
def reset_profiler(): def reset_profiler():
""" """
Clear the previous time record. It works for Clear the previous time record. It works for
...@@ -131,31 +145,38 @@ def reset_profiler(): ...@@ -131,31 +145,38 @@ def reset_profiler():
core.reset_profiler() core.reset_profiler()
@deprecated(
since="2.3.0",
update_to="paddle.profiler.Profiler",
level=1,
reason="Please use new profiler tool, this profiler tool is no longer maintained."
)
def start_profiler(state, tracer_option='Default'): def start_profiler(state, tracer_option='Default'):
""" """
Enable the profiler. Uers can use `fluid.profiler.start_profiler` and Enable the profiler. Uers can use `fluid.profiler.start_profiler` and
`fluid.profiler.stop_profiler` to profile, which is equal to the usage `fluid.profiler.stop_profiler` to profile, which is equal to the usage
of `fluid.profiler.profiler` interface. of `fluid.profiler.profiler` interface.
Args: Args:
state (str) : The profiling state, which should be one of 'CPU', 'GPU' state (str) : The profiling state, which should be one of 'CPU', 'GPU'
or 'All'. 'CPU' means only profiling CPU; 'GPU' means profiling or 'All'. 'CPU' means only profiling CPU; 'GPU' means profiling
both CPU and GPU; 'All' means profiling both CPU and GPU, and both CPU and GPU; 'All' means profiling both CPU and GPU, and
generates timeline as well. generates timeline as well.
tracer_option (str, optional) : tracer_option can be one of ['Default', 'OpDetail', 'AllOpDetail'], it tracer_option (str, optional) : tracer_option can be one of ['Default', 'OpDetail', 'AllOpDetail'], it
can control the profile level and print the different level profile result. `Default` option print can control the profile level and print the different level profile result. `Default` option print
the different Op type profiling result and the `OpDetail` option print the detail profiling the different Op type profiling result and the `OpDetail` option print the detail profiling
result of different op types such as compute and data transform, `AllOpDetail` option result of different op types such as compute and data transform, `AllOpDetail` option
print the detail profiling result of different op name same as `OpDetail`. print the detail profiling result of different op name same as `OpDetail`.
Raises: Raises:
ValueError: If `state` is not in ['CPU', 'GPU', 'All'] or `tracer_option` ValueError: If `state` is not in ['CPU', 'GPU', 'All'] or `tracer_option`
is not in ['Default', 'OpDetail', 'AllOpDetail']. is not in ['Default', 'OpDetail', 'AllOpDetail'].
Examples: Examples:
.. code-block:: python .. code-block:: python
# required: gpu
import paddle.fluid as fluid import paddle.fluid as fluid
import paddle.fluid.profiler as profiler import paddle.fluid.profiler as profiler
...@@ -165,7 +186,7 @@ def start_profiler(state, tracer_option='Default'): ...@@ -165,7 +186,7 @@ def start_profiler(state, tracer_option='Default'):
profiler.reset_profiler() profiler.reset_profiler()
# except each iteration # except each iteration
profiler.stop_profiler('total', '/tmp/profile') profiler.stop_profiler('total', '/tmp/profile')
profiler.start_profiler('GPU', "OpDetail") profiler.start_profiler('GPU', "OpDetail")
for iter in range(10): for iter in range(10):
if iter == 2: if iter == 2:
...@@ -198,14 +219,20 @@ def start_profiler(state, tracer_option='Default'): ...@@ -198,14 +219,20 @@ def start_profiler(state, tracer_option='Default'):
core.enable_profiler(prof_state) core.enable_profiler(prof_state)
@deprecated(
since="2.3.0",
update_to="paddle.profiler.Profiler",
level=1,
reason="Please use new profiler tool, this profiler tool is no longer maintained."
)
def stop_profiler(sorted_key=None, profile_path='/tmp/profile'): def stop_profiler(sorted_key=None, profile_path='/tmp/profile'):
""" """
Stop the profiler. Uers can use `fluid.profiler.start_profiler` and Stop the profiler. Uers can use `fluid.profiler.start_profiler` and
`fluid.profiler.stop_profiler` to profile, which is equal to the usage `fluid.profiler.stop_profiler` to profile, which is equal to the usage
of `fluid.profiler.profiler` interface. of `fluid.profiler.profiler` interface.
Args: Args:
sorted_key (str, optional) : The order of profiling results, which sorted_key (str, optional) : The order of profiling results, which
should be one of None, 'calls', 'total', 'max', 'min' or 'ave'. should be one of None, 'calls', 'total', 'max', 'min' or 'ave'.
Default is None, means the profiling results will be printed Default is None, means the profiling results will be printed
in the order of first end time of events. in the order of first end time of events.
...@@ -214,7 +241,7 @@ def stop_profiler(sorted_key=None, profile_path='/tmp/profile'): ...@@ -214,7 +241,7 @@ def stop_profiler(sorted_key=None, profile_path='/tmp/profile'):
The `max` means sorting by the maximum execution time. The `max` means sorting by the maximum execution time.
The `min` means sorting by the minimum execution time. The `min` means sorting by the minimum execution time.
The `ave` means sorting by the average execution time. The `ave` means sorting by the average execution time.
and write it into `profile_path`. The default profile_path is `/tmp/profile`. and write it into `profile_path`. The default profile_path is `/tmp/profile`.
profile_path (str, optional) : If state == 'All', it will generate timeline, profile_path (str, optional) : If state == 'All', it will generate timeline,
Raises: Raises:
...@@ -225,6 +252,7 @@ def stop_profiler(sorted_key=None, profile_path='/tmp/profile'): ...@@ -225,6 +252,7 @@ def stop_profiler(sorted_key=None, profile_path='/tmp/profile'):
.. code-block:: python .. code-block:: python
# required: gpu
import paddle.fluid as fluid import paddle.fluid as fluid
import paddle.fluid.profiler as profiler import paddle.fluid.profiler as profiler
...@@ -254,6 +282,12 @@ def stop_profiler(sorted_key=None, profile_path='/tmp/profile'): ...@@ -254,6 +282,12 @@ def stop_profiler(sorted_key=None, profile_path='/tmp/profile'):
core.disable_profiler(key_map[sorted_key], profile_path) core.disable_profiler(key_map[sorted_key], profile_path)
@deprecated(
since="2.3.0",
update_to="paddle.profiler.Profiler",
level=1,
reason="Please use new profiler tool, this profiler tool is no longer maintained."
)
@signature_safe_contextmanager @signature_safe_contextmanager
def profiler(state, def profiler(state,
sorted_key=None, sorted_key=None,
...@@ -265,9 +299,9 @@ def profiler(state, ...@@ -265,9 +299,9 @@ def profiler(state,
Args: Args:
state (str) : The profiling state, which should be one of 'CPU', 'GPU' state (str) : The profiling state, which should be one of 'CPU', 'GPU'
or 'All'. 'CPU' means only profiling CPU; 'GPU' means profiling or 'All'. 'CPU' means only profiling CPU; 'GPU' means profiling
both CPU and GPU; 'All' means profiling both CPU and GPU, and both CPU and GPU; 'All' means profiling both CPU and GPU, and
generates timeline as well. generates timeline as well.
sorted_key (str, optional) : The order of profiling results, which sorted_key (str, optional) : The order of profiling results, which
should be one of None, 'calls', 'total', 'max', 'min' or 'ave'. should be one of None, 'calls', 'total', 'max', 'min' or 'ave'.
Default is None, means the profiling results will be printed Default is None, means the profiling results will be printed
in the order of first end time of events. in the order of first end time of events.
...@@ -277,11 +311,11 @@ def profiler(state, ...@@ -277,11 +311,11 @@ def profiler(state,
The `min` means sorting by the minimum execution time. The `min` means sorting by the minimum execution time.
The `ave` means sorting by the average execution time. The `ave` means sorting by the average execution time.
profile_path (str, optional) : If state == 'All', it will generate timeline, profile_path (str, optional) : If state == 'All', it will generate timeline,
and write it into `profile_path`. The default profile_path is `/tmp/profile`. and write it into `profile_path`. The default profile_path is `/tmp/profile`.
tracer_option (str, optional) : tracer_option can be one of ['Default', 'OpDetail', 'AllOpDetail'], it tracer_option (str, optional) : tracer_option can be one of ['Default', 'OpDetail', 'AllOpDetail'], it
can control the profile level and print the different level profile result. `Default` option print can control the profile level and print the different level profile result. `Default` option print
the different Op type profiling result and the `OpDetail` option print the detail profiling the different Op type profiling result and the `OpDetail` option print the detail profiling
result of different op types such as compute and data transform, `AllOpDetail` option result of different op types such as compute and data transform, `AllOpDetail` option
print the detail profiling result of different op name same as `OpDetail`. print the detail profiling result of different op name same as `OpDetail`.
Raises: Raises:
...@@ -319,7 +353,7 @@ def profiler(state, ...@@ -319,7 +353,7 @@ def profiler(state,
#### Examples Results #### #### Examples Results ####
#### 1) sorted_key = 'total', 'calls', 'max', 'min', 'ave' #### #### 1) sorted_key = 'total', 'calls', 'max', 'min', 'ave' ####
# The only difference in 5 sorted_key results is the following sentence: # The only difference in 5 sorted_key results is the following sentence:
# "Sorted by number of xxx in descending order in the same thread." # "Sorted by number of xxx in descending order in the same thread."
# The reason is that in this example, above 5 columns are already sorted. # The reason is that in this example, above 5 columns are already sorted.
-------------------------> Profiling Report <------------------------- -------------------------> Profiling Report <-------------------------
...@@ -339,7 +373,7 @@ def profiler(state, ...@@ -339,7 +373,7 @@ def profiler(state,
#### 2) sorted_key = None #### #### 2) sorted_key = None ####
# Since the profiling results are printed in the order of first end time of Ops, # Since the profiling results are printed in the order of first end time of Ops,
# the printed order is feed->conv2d->elementwise_add # the printed order is feed->conv2d->elementwise_add
-------------------------> Profiling Report <------------------------- -------------------------> Profiling Report <-------------------------
Place: CPU Place: CPU
...@@ -366,7 +400,7 @@ def _nvprof_range(iter_id, start, end, exit_after_prof=True): ...@@ -366,7 +400,7 @@ def _nvprof_range(iter_id, start, end, exit_after_prof=True):
Examples: Examples:
.. code-block:: python .. code-block:: python
model = Model() model = Model()
for i in range(max_iter): for i in range(max_iter):
paddle.fluid.profiler._nvprof_range(i, 10, 20): paddle.fluid.profiler._nvprof_range(i, 10, 20):
......
...@@ -56,7 +56,15 @@ class TestProfilerStatistic(unittest.TestCase): ...@@ -56,7 +56,15 @@ class TestProfilerStatistic(unittest.TestCase):
mobilenet_node = HostPythonNode( mobilenet_node = HostPythonNode(
'MobileNet', profiler.TracerEventType.Forward, 20, 50, 1000, 1001) 'MobileNet', profiler.TracerEventType.Forward, 20, 50, 1000, 1001)
yolonet_node = HostPythonNode( yolonet_node = HostPythonNode(
'Yolov3Net', profiler.TracerEventType.Forward, 50, 100, 1000, 1001) 'Yolov3Net', profiler.TracerEventType.Forward, 50, 110, 1000, 1001)
userdefined_node = HostPythonNode('Communication Time',
profiler.TracerEventType.UserDefined,
100, 110, 1000, 1001)
communication_node = HostPythonNode(
'Communication', profiler.TracerEventType.Communication, 105, 110,
1000, 1001)
backward_node = HostPythonNode('Gradient Backward', backward_node = HostPythonNode('Gradient Backward',
profiler.TracerEventType.Backward, 120, profiler.TracerEventType.Backward, 120,
200, 1000, 1001) 200, 1000, 1001)
...@@ -114,7 +122,9 @@ class TestProfilerStatistic(unittest.TestCase): ...@@ -114,7 +122,9 @@ class TestProfilerStatistic(unittest.TestCase):
optimization_node optimization_node
]) ])
mobilenet_node.children_node.append(conv2d_node) mobilenet_node.children_node.append(conv2d_node)
yolonet_node.children_node.append(sync_batch_norm_node) yolonet_node.children_node.extend(
[sync_batch_norm_node, userdefined_node])
userdefined_node.children_node.append(communication_node)
conv2d_node.children_node.extend( conv2d_node.children_node.extend(
[conv2d_infer_shape, conv2d_compute, conv2d_MemCpy]) [conv2d_infer_shape, conv2d_compute, conv2d_MemCpy])
conv2d_compute.runtime_node.append(conv2d_launchkernel) conv2d_compute.runtime_node.append(conv2d_launchkernel)
...@@ -145,7 +155,7 @@ class TestProfilerStatistic(unittest.TestCase): ...@@ -145,7 +155,7 @@ class TestProfilerStatistic(unittest.TestCase):
profiler.TracerEventType.ProfileStep), 400) profiler.TracerEventType.ProfileStep), 400)
self.assertEqual( self.assertEqual(
time_range_summary.get_cpu_range_sum( time_range_summary.get_cpu_range_sum(
profiler.TracerEventType.Forward), 90) profiler.TracerEventType.Forward), 100)
self.assertEqual( self.assertEqual(
time_range_summary.get_cpu_range_sum( time_range_summary.get_cpu_range_sum(
profiler.TracerEventType.Backward), 80) profiler.TracerEventType.Backward), 80)
...@@ -169,15 +179,18 @@ class TestProfilerStatistic(unittest.TestCase): ...@@ -169,15 +179,18 @@ class TestProfilerStatistic(unittest.TestCase):
0, profiler.TracerEventType.Memcpy), 60) 0, profiler.TracerEventType.Memcpy), 60)
self.assertEqual( self.assertEqual(
time_range_summary.get_cpu_range_sum( time_range_summary.get_cpu_range_sum(
profiler.TracerEventType.UserDefined), 15) profiler.TracerEventType.UserDefined), 25)
self.assertEqual(
time_range_summary.get_cpu_range_sum(
profiler.TracerEventType.Communication), 5)
self.assertEqual(len(event_summary.items), 2) self.assertEqual(len(event_summary.items), 2)
self.assertEqual(len(event_summary.userdefined_items), 0) self.assertEqual(len(event_summary.userdefined_items), 1)
self.assertEqual(len(event_summary.model_perspective_items), 3) self.assertEqual(len(event_summary.model_perspective_items), 3)
self.assertEqual(len(event_summary.memory_manipulation_items), 1) self.assertEqual(len(event_summary.memory_manipulation_items), 1)
self.assertEqual(event_summary.items['conv2d'].cpu_time, 15) self.assertEqual(event_summary.items['conv2d'].cpu_time, 15)
self.assertEqual(event_summary.items['conv2d'].gpu_time, 25) self.assertEqual(event_summary.items['conv2d'].gpu_time, 25)
self.assertEqual( self.assertEqual(
event_summary.model_perspective_items['Forward'].cpu_time, 90) event_summary.model_perspective_items['Forward'].cpu_time, 100)
self.assertEqual( self.assertEqual(
event_summary.model_perspective_items['Forward'].gpu_time, 135) event_summary.model_perspective_items['Forward'].gpu_time, 135)
self.assertEqual( self.assertEqual(
......
...@@ -20,7 +20,7 @@ from .utils import RecordEvent, load_profiler_result ...@@ -20,7 +20,7 @@ from .utils import RecordEvent, load_profiler_result
from .profiler_statistic import SortedKeys from .profiler_statistic import SortedKeys
__all__ = [ __all__ = [
'ProfilerState', 'ProfilerTarget', 'TracerEventType', 'make_scheduler', 'ProfilerState', 'ProfilerTarget', 'make_scheduler',
'export_chrome_tracing', 'export_protobuf', 'Profiler', 'RecordEvent', 'export_chrome_tracing', 'export_protobuf', 'Profiler', 'RecordEvent',
'load_profiler_result', 'SortedKeys' 'load_profiler_result', 'SortedKeys'
] ]
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. # Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
...@@ -24,7 +24,7 @@ from paddle.fluid.core import (_Profiler, _ProfilerResult, ProfilerOptions, ...@@ -24,7 +24,7 @@ from paddle.fluid.core import (_Profiler, _ProfilerResult, ProfilerOptions,
TracerEventType) TracerEventType)
from .utils import RecordEvent, wrap_optimizers from .utils import RecordEvent, wrap_optimizers
from .profiler_statistic import SortedKeys from .profiler_statistic import StatisticData, _build_table, SortedKeys
class ProfilerState(Enum): class ProfilerState(Enum):
...@@ -32,21 +32,28 @@ class ProfilerState(Enum): ...@@ -32,21 +32,28 @@ class ProfilerState(Enum):
Profiler state that can be specified to control profiler action. Profiler state that can be specified to control profiler action.
CLOSED: The profilers are closed. CLOSED: The profilers are closed.
READY: The profilers are open, but the data will not be recorded. READY: The profilers are open, but the data will not be recorded.
This state is used for reducing overhead influence when profilers start. This state is used for reducing overhead influence when profilers start.
RECORD: The profilers are open, and the data will be recorded. RECORD: The profilers are open, and the data will be recorded.
RECORD_AND_RETURN: The profilers are open, and at the last batch of current profiler period,
the collected data will be returned. RECORD_AND_RETURN: The profilers are open, and at the last batch of current profiler period,
the collected data will be returned.
""" """
CLOSED = 0 CLOSED = 0
READY = 1 READY = 1
RECORD = 2 RECORD = 2
RECORD_AND_RETURN = 3 # the last step of RECORD RECORD_AND_RETURN = 3 # the last step of RECORD
class ProfilerTarget(Enum): class ProfilerTarget(Enum):
r""" r"""
Target device for profiling. Target device for profiling.
CPU: Profile events on CPU.
GPU: Profile events on GPU.
""" """
CPU = 0 CPU = 0
GPU = 1 GPU = 1
...@@ -62,17 +69,19 @@ def make_scheduler(*, ...@@ -62,17 +69,19 @@ def make_scheduler(*,
Return a scheduler function, which scheduler the state according to the setting. Return a scheduler function, which scheduler the state according to the setting.
The state transform confirms to: The state transform confirms to:
(CLOSED) (CLOSED) (CLOSED) (READY) (RECORD,last RETURN) (CLOSED) .. code-block:: text
START -> skip_first -> closed -> ready -> record -> END
| | (CLOSED) (CLOSED) (CLOSED) (READY) (RECORD,last RETURN) (CLOSED)
| | (if has_repeated < repeat) START -> skip_first -> closed -> ready -> record -> END
- - - - - - - - - - - - | |
Note that repeat <= 0 means the cycle will continue until the profiler exits. | | (if has_repeated < repeat)
- - - - - - - - - - - -
Note that repeat <= 0 means the cycle will continue until the profiler exits.
Parameters: Parameters:
closed(int): The number of steps in state ProfilerState.CLOSED. closed(int): The number of steps in state ProfilerState.CLOSED.
ready(int): The number of steps in state ProfilerState.READY. ready(int): The number of steps in state ProfilerState.READY.
record(int): The number of steps in state ProfilerState.RECORD. record(int): The number of steps in state ProfilerState.RECORD.
repeat(int): The number of cycles to repeat above state transform. repeat(int): The number of cycles to repeat above state transform.
skip_first(int): The number of first steps to drop, not participate in the state transform. skip_first(int): The number of first steps to drop, not participate in the state transform.
...@@ -81,13 +90,23 @@ def make_scheduler(*, ...@@ -81,13 +90,23 @@ def make_scheduler(*,
Examples: Examples:
1. profiling range [2, 5] 1. profiling range [2, 5]
batch 0: closed, batch 1: ready, batch [2, 5] record batch 0: closed, batch 1: ready, batch [2, 5] record
.. code-block:: python
make_scheduler(closed=1, ready=1, record=4, repeat=1) .. code-block:: python
import paddle.profiler as profiler
profiler.make_scheduler(closed=1, ready=1, record=4, repeat=1)
2. profiling range [3,6], [9,12], [15,18]... 2. profiling range [3,6], [9,12], [15,18]...
batch 0: skiped, batch 1: closed, batch 2: ready, batch [3,6]: record, repeat batch 0: skiped, batch 1: closed, batch 2: ready, batch [3,6]: record, repeat
.. code-block:: python
make_scheduler(closed=1, ready=1, record=4, skip_first=1) .. code-block:: python
import paddle.profiler as profiler
profiler.make_scheduler(closed=1, ready=1, record=4, skip_first=1)
""" """
def getScheduleState(step: int) -> ProfilerState: def getScheduleState(step: int) -> ProfilerState:
...@@ -138,15 +157,16 @@ def export_chrome_tracing(dir_name: str, ...@@ -138,15 +157,16 @@ def export_chrome_tracing(dir_name: str,
Examples: Examples:
.. code-block:: python .. code-block:: python
import paddle.profiler as profiler
with profiler.Profiler(targets=[profiler.ProfilerTarget.CPU, # required: gpu
profiler.ProfilerTarget.GPU], import paddle.profiler as profiler
scheduler = (3, 10), with profiler.Profiler(
on_trace_ready = profiler.export_chrome_tracing('./log') targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
) as p: scheduler = (3, 10),
for iter in range(N): on_trace_ready=profiler.export_protobuf('./log')) as p:
train() for iter in range(10):
p.step() #train()
p.step()
""" """
if not os.path.exists(dir_name): if not os.path.exists(dir_name):
try: try:
...@@ -181,15 +201,16 @@ def export_protobuf(dir_name: str, worker_name: Optional[str]=None) -> Callable: ...@@ -181,15 +201,16 @@ def export_protobuf(dir_name: str, worker_name: Optional[str]=None) -> Callable:
Examples: Examples:
.. code-block:: python .. code-block:: python
import paddle.profiler as profiler
with profiler.Profiler(targets=[profiler.ProfilerTarget.CPU, # required: gpu
profiler.ProfilerTarget.GPU], import paddle.profiler as profiler
scheduler = (3, 10), with profiler.Profiler(
on_trace_ready = profiler.export_protobuf('./log') targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
) as p: scheduler = (3, 10),
for iter in range(N): on_trace_ready = profiler.export_protobuf('./log')) as p:
train() for iter in range(10):
p.step() #train()
p.step()
""" """
if not os.path.exists(dir_name): if not os.path.exists(dir_name):
try: try:
...@@ -216,7 +237,7 @@ def _get_supported_targets() -> Iterable[ProfilerTarget]: ...@@ -216,7 +237,7 @@ def _get_supported_targets() -> Iterable[ProfilerTarget]:
r""" r"""
Get the current supported profiler target in the system. Get the current supported profiler target in the system.
""" """
if paddle.device.is_compiled_with_cuda(): if _Profiler.is_cupti_supported():
return [ProfilerTarget.CPU, ProfilerTarget.GPU] return [ProfilerTarget.CPU, ProfilerTarget.GPU]
return [ProfilerTarget.CPU] return [ProfilerTarget.CPU]
...@@ -226,48 +247,56 @@ class Profiler: ...@@ -226,48 +247,56 @@ class Profiler:
Profiler context manager, user interface to manage profile process. Profiler context manager, user interface to manage profile process.
Parameters: Parameters:
targets (iterable): list of tracing targets, currently supported values: targets (iterable): list of tracing targets, currently supported values, ``ProfilerTarget.CPU``, ``ProfilerTarget.GPU`` .
``paddle.profiler.ProfilerTarget.CPU``, scheduler (callable or tuple): If it is a callable object, it takes a step number as parameter and return the corresponding ``ProfilerState``.
``paddle.profiler.ProfilerTarget.GPU``. If not provided, the default scheduler will keep tracing until the profiler exits. If it is a tuple, it has two values start_batch and end_batch,
scheduler (callable or tuple): If it is a callable object, it takes a step number as parameter and return the corresponding ``ProfilerState``.
If not provided, the default sheduler will keep tracing until the profiler exits. If it is a tuple, it has two values start_batch and end_batch,
which means profiling range [start_batch, end_batch). which means profiling range [start_batch, end_batch).
on_trace_ready (callable): callable object, takes the Profiler object as parameter, which provides a way for users to do post-processing. on_trace_ready (callable): callable object, takes the Profiler object as parameter, which provides a way for users to do post-processing.
This callable object will be called when ``sheduler`` returns ``ProfilerState.RECORD_AND_RETURN``. This callable object will be called when ``scheduler`` returns ``ProfilerState.RECORD_AND_RETURN``.
Examples: Examples:
1. profiling range [2, 5) 1. profiling range [2, 5)
.. code-block:: python
import paddle.profiler as profiler .. code-block:: python
with profiler.Profiler(targets=[profiler.ProfilerTarget.CPU,
profiler.ProfilerTarget.GPU], # required: gpu
scheduler = (2, 5), import paddle.profiler as profiler
on_trace_ready = profiler.export_chrome_tracing('./log') with profiler.Profiler(
) as p: targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
for iter in range(N): scheduler = (2, 5),
train() on_trace_ready = profiler.export_chrome_tracing('./log')) as p:
p.step() for iter in range(10):
#train()
p.step()
2. profiling range [2,4], [7, 9], [11,13] 2. profiling range [2,4], [7, 9], [11,13]
.. code-block:: python
import paddle.profiler as profiler .. code-block:: python
with profiler.Profiler(targets=[profiler.ProfilerTarget.CPU,
profiler.ProfilerTarget.GPU], # required: gpu
scheduler = profiler.make_scheduler(closed=1, ready=1, record=3, repeat=3), import paddle.profiler as profiler
on_trace_ready = profiler.export_chrome_tracing('./log') with profiler.Profiler(
) as p: targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
for iter in range(N): scheduler = profiler.make_scheduler(closed=1, ready=1, record=3, repeat=3),
train() on_trace_ready = profiler.export_chrome_tracing('./log')) as p:
p.step() for iter in range(10):
#train()
p.step()
3. Use profiler without context manager, and use default parameters 3. Use profiler without context manager, and use default parameters
.. code-block:: python
import paddle.profiler as profiler .. code-block:: python
p = profiler.Profiler()
p.start() # required: gpu
for iter in range(N): import paddle.profiler as profiler
train() p = profiler.Profiler()
p.step() p.start()
p.stop() for iter in range(10):
p.summary() #train()
p.step()
p.stop()
p.summary()
""" """
def __init__( def __init__(
...@@ -334,7 +363,22 @@ class Profiler: ...@@ -334,7 +363,22 @@ class Profiler:
def start(self): def start(self):
r''' r'''
Start profiler and enter the first profiler step(0). Start profiler and enter the first profiler step(0).
State transformed from CLOSED to self.current_state and trigger corresponding action. State transformed from CLOSED to self.current_state and trigger corresponding action.
Examples:
.. code-block:: python
# required: gpu
import paddle.profiler as profiler
prof = profiler.Profiler(
targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
scheduler = (1, 9),
on_trace_ready = profiler.export_chrome_tracing('./log'))
prof.start()
for iter in range(10):
#train()
prof.step()
prof.stop()
''' '''
# CLOSED -> self.current_state # CLOSED -> self.current_state
if self.current_state == ProfilerState.READY: if self.current_state == ProfilerState.READY:
...@@ -354,6 +398,21 @@ class Profiler: ...@@ -354,6 +398,21 @@ class Profiler:
r''' r'''
Stop profiler and State transformed from self.current_state to CLOSED. Stop profiler and State transformed from self.current_state to CLOSED.
Trigger corresponding action and post-process profiler result using self.on_trace_ready if result exists. Trigger corresponding action and post-process profiler result using self.on_trace_ready if result exists.
Examples:
.. code-block:: python
# required: gpu
import paddle.profiler as profiler
prof = profiler.Profiler(
targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
scheduler = (1, 7),
on_trace_ready = profiler.export_chrome_tracing('./log'))
prof.start()
for iter in range(10):
#train()
prof.step()
prof.stop()
''' '''
# self.current_state -> CLOSED # self.current_state -> CLOSED
# In this situation, RECORD state is regarded as RECORD_AND_RETURN # In this situation, RECORD state is regarded as RECORD_AND_RETURN
...@@ -375,6 +434,22 @@ class Profiler: ...@@ -375,6 +434,22 @@ class Profiler:
r""" r"""
Signals the profiler that the next profiling step has started. Signals the profiler that the next profiling step has started.
Get the new ProfilerState and trigger corresponding action. Get the new ProfilerState and trigger corresponding action.
Examples:
.. code-block:: python
# required: gpu
import paddle.profiler as profiler
prof = profiler.Profiler(
targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
scheduler = (3, 7),
on_trace_ready = profiler.export_chrome_tracing('./log'))
prof.start()
for iter in range(10):
#train()
prof.step()
prof.stop()
""" """
if self.record_event: if self.record_event:
self.record_event.end() self.record_event.end()
...@@ -448,6 +523,21 @@ class Profiler: ...@@ -448,6 +523,21 @@ class Profiler:
def export(self, path="", format="json"): def export(self, path="", format="json"):
r""" r"""
Exports the tracing data in Chrome tracing data format. Exports the tracing data in Chrome tracing data format.
Examples:
.. code-block:: python
# required: gpu
import paddle.profiler as profiler
prof = profiler.Profiler(
targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
scheduler = (3, 7))
prof.start()
for iter in range(10):
#train()
prof.step()
prof.stop()
prof.export(path="./profiler_data.json", format="json")
""" """
if self.profiler_result: if self.profiler_result:
self.profiler_result.save(path, format) self.profiler_result.save(path, format)
...@@ -461,9 +551,35 @@ class Profiler: ...@@ -461,9 +551,35 @@ class Profiler:
Print the Summary table. Print the Summary table.
Parameters: Parameters:
sorted_by: how to rank the op table items. sorted_by(SortedKeys): how to rank the op table items.
detail: expand each operator detail information. op_detail(bool): expand each operator detail information.
thread_sep: print op table each thread. thread_sep(bool): print op table each thread.
time_unit: can be chosen form ['s', 'ms', 'us', 'ns'] time_unit(str): can be chosen form ['s', 'ms', 'us', 'ns']
Examples:
.. code-block:: python
# required: gpu
import paddle.profiler as profiler
prof = profiler.Profiler(
targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
scheduler = (3, 7),
on_trace_ready = profiler.export_chrome_tracing('./log'))
prof.start()
for iter in range(10):
#train()
prof.step()
prof.stop()
prof.summary(sorted_by=profiler.SortedKeys.CPUTotal, op_detail=True, thread_sep=False, time_unit='ms')
""" """
pass if self.profiler_result:
statistic_data = StatisticData(
self.profiler_result.get_data(),
self.profiler_result.get_extra_info())
print(
_build_table(
statistic_data,
sorted_by=sorted_by,
op_detail=op_detail,
thread_sep=thread_sep,
time_unit=time_unit))
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. # Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
...@@ -34,6 +34,22 @@ _CommunicationOpName = ['reduce', 'broadcast', 'rpc'] ...@@ -34,6 +34,22 @@ _CommunicationOpName = ['reduce', 'broadcast', 'rpc']
class SortedKeys(Enum): class SortedKeys(Enum):
r""" r"""
Sorted keys for printing summary table. Sorted keys for printing summary table.
CPUTotal: Sorted by CPU total time.
CPUAvg: Sorted by CPU average time.
CPUMax: Sorted by CPU max time.
CPUMin: Sorted by CPU min time.
GPUTotal: Sorted by GPU total time.
GPUAvg: Sorted by GPU average time.
GPUMax: Sorted by GPU max time.
GPUMin: Sorted by GPU min time.
""" """
CPUTotal = 0 CPUTotal = 0
CPUAvg = 1 CPUAvg = 1
...@@ -642,6 +658,171 @@ def _build_table(statistic_data, ...@@ -642,6 +658,171 @@ def _build_table(statistic_data,
append('') append('')
append('') append('')
###### Print Model Summary Report ######
model_perspective_items = statistic_data.event_summary.model_perspective_items
if model_perspective_items:
headers = [
'Name', 'Calls', 'CPU Total / Avg / Max / Min / Ratio(%)',
'GPU Total / Avg / Max / Min / Ratio(%)'
]
row_format_list = [""]
header_sep_list = [""]
line_length_list = [-SPACING_SIZE]
name_column_width = 15
add_column(name_column_width)
add_column(6)
add_column(40)
add_column(40)
row_format = row_format_list[0]
header_sep = header_sep_list[0]
line_length = line_length_list[0]
# construct table string
append(add_title(line_length, "Model Summary"))
append('Time unit: {}'.format(time_unit))
append(header_sep)
append(row_format.format(*headers))
append(header_sep)
accmulation_time = 0
row_values = [
'Total Time', '-', '{} / - / - / - / {}'.format(
format_time(
total_time, unit=time_unit), format_ratio(1)),
'- / - / - / -/ -'
]
append(row_format.format(*row_values))
for name in ['Dataloader', 'Forward', 'Backward', 'Optimization']:
if name in model_perspective_items:
item = model_perspective_items[name]
row_values = [
' {}'.format(name), item.call,
'{} / {} / {} / {} / {}'.format(
format_time(
item.cpu_time, unit=time_unit),
format_time(
item.avg_cpu_time, unit=time_unit),
format_time(
item.max_cpu_time, unit=time_unit),
format_time(
item.min_cpu_time, unit=time_unit),
format_ratio(float(item.cpu_time) / total_time)),
'{} / {} / {} / {} / {}'.format(
format_time(
item.gpu_time, unit=time_unit),
format_time(
item.avg_gpu_time, unit=time_unit),
format_time(
item.max_gpu_time, unit=time_unit),
format_time(
item.min_gpu_time, unit=time_unit),
format_ratio(float(item.gpu_time) / total_time))
]
append(row_format.format(*row_values))
accmulation_time += item.cpu_time
other_time = total_time - accmulation_time
row_values = [
' Others', '-', '{} / - / - / - / {}'.format(
format_time(
other_time, unit=time_unit),
format_ratio(float(other_time) / total_time)),
'- / - / - / - / -'
]
append(row_format.format(*row_values))
append(header_sep)
append('')
append('')
###### Print Distribution Summary Report ######
if TracerEventType.Communication in statistic_data.time_range_summary.CPUTimeRange:
headers = [
'Name',
'Total Time',
'Ratio (%)',
]
row_format_list = [""]
header_sep_list = [""]
line_length_list = [-SPACING_SIZE]
DEFAULT_COLUMN_WIDTH = 20
for _ in headers:
add_column(DEFAULT_COLUMN_WIDTH)
row_format = row_format_list[0]
header_sep = header_sep_list[0]
line_length = line_length_list[0]
# construct table string
append(add_title(line_length, "Distribution Summary"))
append('Time unit: {}'.format(time_unit))
append(header_sep)
append(row_format.format(*headers))
append(header_sep)
cpu_communication_time_range = []
gpu_communication_time_range = []
cpu_communication_time_range = merge_ranges(
statistic_data.time_range_summary.CPUTimeRange[
TracerEventType.Communication], cpu_communication_time_range)
kernel_time_range = []
for device_id, device_time_ranges in statistic_data.time_range_summary.GPUTimeRange.items(
):
kernel_time_range = merge_ranges(
device_time_ranges[TracerEventType.Kernel],
kernel_time_range,
is_sorted=True)
gpu_communication_time_range = merge_ranges(
device_time_ranges[TracerEventType.Communication],
gpu_communication_time_range,
is_sorted=True)
communication_time_range = merge_ranges(
cpu_communication_time_range,
gpu_communication_time_range,
is_sorted=True)
computation_time_range = subtract_ranges(kernel_time_range,
gpu_communication_time_range)
overlap_time_range = intersection_ranges(communication_time_range,
computation_time_range)
communication_time = sum_ranges(communication_time_range)
computation_time = sum_ranges(computation_time_range)
overlap_time = sum_ranges(overlap_time_range)
row_values = [
'Communication', format_time(
communication_time, unit=time_unit),
format_ratio(float(communication_time) / total_time)
]
append(row_format.format(*row_values))
row_values = [
'Computation', format_time(
computation_time, unit=time_unit),
format_ratio(float(computation_time) / total_time)
]
append(row_format.format(*row_values))
row_values = [
'Overlap', format_time(
overlap_time, unit=time_unit),
format_ratio(float(overlap_time) / total_time)
]
append(row_format.format(*row_values))
append(header_sep)
append(
"Note:\nCommunication time: Communication Op time and its kernel time on gpu.\n"
"Computation time: Kernel time, substract kernels belong to communication op.\n"
"Overlap time: Communication time intersect with computation time.\n"
"Example:\n"
"Communication:\n"
" CPU: |_________________|\n"
" GPU: |______________|\n"
" Total: |_________________| |______________|\n"
"Computation time(Kernel):\n"
" GPU: |________________|\n"
"Overlap time: |___________|\n")
append('-' * line_length)
append('')
append('')
###### Print Operator Summary Report ###### ###### Print Operator Summary Report ######
if statistic_data.event_summary.items: if statistic_data.event_summary.items:
headers = [ headers = [
...@@ -708,11 +889,6 @@ def _build_table(statistic_data, ...@@ -708,11 +889,6 @@ def _build_table(statistic_data,
sorted_items = sorted( sorted_items = sorted(
items.items(), key=lambda x: x[1].min_gpu_time) items.items(), key=lambda x: x[1].min_gpu_time)
total_cpu_time = 0
total_gpu_time = 0
for name, item in sorted_items:
total_cpu_time += item.cpu_time
total_gpu_time += item.gpu_time
for name, item in sorted_items: for name, item in sorted_items:
row_values = [ row_values = [
name, item.call, '{} / {} / {} / {} / {}'.format( name, item.call, '{} / {} / {} / {} / {}'.format(
...@@ -724,7 +900,7 @@ def _build_table(statistic_data, ...@@ -724,7 +900,7 @@ def _build_table(statistic_data,
item.max_cpu_time, unit=time_unit), item.max_cpu_time, unit=time_unit),
format_time( format_time(
item.min_cpu_time, unit=time_unit), item.min_cpu_time, unit=time_unit),
format_ratio(float(item.cpu_time) / total_cpu_time)), format_ratio(float(item.cpu_time) / total_time)),
'{} / {} / {} / {} / {}'.format( '{} / {} / {} / {} / {}'.format(
format_time( format_time(
item.gpu_time, unit=time_unit), item.gpu_time, unit=time_unit),
...@@ -734,7 +910,7 @@ def _build_table(statistic_data, ...@@ -734,7 +910,7 @@ def _build_table(statistic_data,
item.max_gpu_time, unit=time_unit), item.max_gpu_time, unit=time_unit),
format_time( format_time(
item.min_gpu_time, unit=time_unit), item.min_gpu_time, unit=time_unit),
format_ratio(float(item.gpu_time) / total_gpu_time)) format_ratio(float(item.gpu_time) / total_time))
] ]
append(row_format.format(*row_values)) append(row_format.format(*row_values))
if op_detail: if op_detail:
...@@ -752,8 +928,7 @@ def _build_table(statistic_data, ...@@ -752,8 +928,7 @@ def _build_table(statistic_data,
format_time( format_time(
innerop_node.min_cpu_time, unit=time_unit), innerop_node.min_cpu_time, unit=time_unit),
format_ratio( format_ratio(
float(innerop_node.cpu_time) / float(innerop_node.cpu_time) / total_time)),
total_cpu_time)),
'{} / {} / {} / {} / {}'.format( '{} / {} / {} / {} / {}'.format(
format_time( format_time(
innerop_node.gpu_time, unit=time_unit), innerop_node.gpu_time, unit=time_unit),
...@@ -764,8 +939,7 @@ def _build_table(statistic_data, ...@@ -764,8 +939,7 @@ def _build_table(statistic_data,
format_time( format_time(
innerop_node.min_gpu_time, unit=time_unit), innerop_node.min_gpu_time, unit=time_unit),
format_ratio( format_ratio(
float(innerop_node.gpu_time) / float(innerop_node.gpu_time) / total_time))
total_gpu_time))
] ]
append(row_format.format(*row_values)) append(row_format.format(*row_values))
for device_node_name, devicenode in innerop_node.devices.items( for device_node_name, devicenode in innerop_node.devices.items(
...@@ -792,7 +966,7 @@ def _build_table(statistic_data, ...@@ -792,7 +966,7 @@ def _build_table(statistic_data,
unit=time_unit), unit=time_unit),
format_ratio( format_ratio(
float(devicenode.gpu_time) / float(devicenode.gpu_time) /
total_gpu_time)) total_time))
] ]
append(row_format.format(*row_values)) append(row_format.format(*row_values))
for device_node_name, device_node in item.devices.items(): for device_node_name, device_node in item.devices.items():
...@@ -814,11 +988,160 @@ def _build_table(statistic_data, ...@@ -814,11 +988,160 @@ def _build_table(statistic_data,
format_time( format_time(
devicenode.min_gpu_time, unit=time_unit), devicenode.min_gpu_time, unit=time_unit),
format_ratio( format_ratio(
float(devicenode.gpu_time) / float(devicenode.gpu_time) / total_time))
total_gpu_time))
] ]
append(row_format.format(*row_values)) append(row_format.format(*row_values))
append(header_sep) append(header_sep)
append('') append('')
append('') append('')
###### Print Memory Manipulation Summary Report ######
if statistic_data.event_summary.memory_manipulation_items:
headers = [
'Name', 'Calls', 'CPU Total / Avg / Max / Min / Ratio(%)',
'GPU Total / Avg / Max / Min / Ratio(%)'
]
row_format_list = [""]
header_sep_list = [""]
line_length_list = [-SPACING_SIZE]
name_column_width = 30
add_column(name_column_width)
add_column(6)
add_column(40)
add_column(40)
row_format = row_format_list[0]
header_sep = header_sep_list[0]
line_length = line_length_list[0]
# construct table string
append(add_title(line_length, "Memory Manipulation Summary"))
append('Time unit: {}'.format(time_unit))
append(header_sep)
append(row_format.format(*headers))
append(header_sep)
memory_manipulation_items = statistic_data.event_summary.memory_manipulation_items
for name, item in memory_manipulation_items.items():
row_values = [
name,
item.call,
'{} / {} / {} / {} / {}'.format(
format_time(
item.cpu_time, unit=time_unit),
format_time(
item.avg_cpu_time, unit=time_unit),
format_time(
item.max_cpu_time, unit=time_unit),
format_time(
item.min_cpu_time, unit=time_unit),
format_ratio(float(item.cpu_time) / total_time)),
'{} / {} / {} / {} / {}'.format(
format_time(
item.gpu_time, unit=time_unit),
format_time(
item.avg_gpu_time, unit=time_unit),
format_time(
item.max_gpu_time, unit=time_unit),
format_time(
item.min_gpu_time, unit=time_unit),
format_ratio(float(item.gpu_time) / total_time)),
]
append(row_format.format(*row_values))
append(header_sep)
append('')
append('')
###### Print UserDefined Summary Report ######
if statistic_data.event_summary.userdefined_items:
headers = [
'Name', 'Calls', 'CPU Total / Avg / Max / Min / Ratio(%)',
'GPU Total / Avg / Max / Min / Ratio(%)'
]
row_format_list = [""]
header_sep_list = [""]
line_length_list = [-SPACING_SIZE]
name_column_width = 30
add_column(name_column_width)
add_column(6)
add_column(40)
add_column(40)
row_format = row_format_list[0]
header_sep = header_sep_list[0]
line_length = line_length_list[0]
# construct table string
append(add_title(line_length, "UserDefined Summary"))
append('Time unit: {}'.format(time_unit))
append(header_sep)
append(row_format.format(*headers))
append(header_sep)
if thread_sep == True:
userdefined_thread_items = statistic_data.event_summary.userdefined_thread_items
else:
userdefined_thread_items = {
'All threads merged':
statistic_data.event_summary.userdefined_items
}
for thread_id, items in userdefined_thread_items.items():
append(add_title(line_length, "Thread: {}".format(thread_id)))
if sorted_by == SortedKeys.CPUTotal:
sorted_items = sorted(
items.items(), key=lambda x: x[1].cpu_time, reverse=True)
elif sorted_by == SortedKeys.CPUAvg:
sorted_items = sorted(
items.items(),
key=lambda x: x[1].avg_cpu_time,
reverse=True)
elif sorted_by == SortedKeys.CPUMax:
sorted_items = sorted(
items.items(),
key=lambda x: x[1].max_cpu_time,
reverse=True)
elif sorted_by == SortedKeys.CPUMin:
sorted_items = sorted(
items.items(), key=lambda x: x[1].min_cpu_time)
elif sorted_by == SortedKeys.GPUTotal:
sorted_items = sorted(
items.items(), key=lambda x: x[1].gpu_time, reverse=True)
elif sorted_by == SortedKeys.GPUAvg:
sorted_items = sorted(
items.items(),
key=lambda x: x[1].avg_gpu_time,
reverse=True)
elif sorted_by == SortedKeys.GPUMax:
sorted_items = sorted(
items.items(),
key=lambda x: x[1].max_gpu_time,
reverse=True)
elif sorted_by == SortedKeys.GPUMin:
sorted_items = sorted(
items.items(), key=lambda x: x[1].min_gpu_time)
for name, item in sorted_items:
row_values = [
name,
item.call,
'{} / {} / {} / {} / {}'.format(
format_time(
item.cpu_time, unit=time_unit),
format_time(
item.avg_cpu_time, unit=time_unit),
format_time(
item.max_cpu_time, unit=time_unit),
format_time(
item.min_cpu_time, unit=time_unit),
format_ratio(float(item.cpu_time) / total_time)),
'{} / {} / {} / {} / {}'.format(
format_time(
item.gpu_time, unit=time_unit),
format_time(
item.avg_gpu_time, unit=time_unit),
format_time(
item.max_gpu_time, unit=time_unit),
format_time(
item.min_gpu_time, unit=time_unit),
format_ratio(float(item.gpu_time) / total_time)),
]
append(row_format.format(*row_values))
append(header_sep)
return ''.join(result) return ''.join(result)
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved. # Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
# You may obtain a copy of the License at # You may obtain a copy of the License at
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, # distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from paddle.fluid.core import (_RecordEvent, TracerEventType,
load_profiler_result)
from typing import Any from typing import Any
from warnings import warn from warnings import warn
import functools import functools
from contextlib import ContextDecorator from contextlib import ContextDecorator
from paddle.fluid.core import (_RecordEvent, TracerEventType)
import paddle.fluid.core as core
_AllowedEventTypeList = [ _AllowedEventTypeList = [
TracerEventType.Dataloader, TracerEventType.ProfileStep, TracerEventType.Dataloader, TracerEventType.ProfileStep,
TracerEventType.UserDefined, TracerEventType.Forward, TracerEventType.UserDefined, TracerEventType.Forward,
...@@ -32,14 +33,28 @@ class RecordEvent(ContextDecorator): ...@@ -32,14 +33,28 @@ class RecordEvent(ContextDecorator):
Interface for recording a time range. Interface for recording a time range.
Parameters: Parameters:
name(str): Name of the record event name(str): Name of the record event
event_type(TracerEventType): Type of the record event, can be used for statistics.
Examples: Examples:
.. code-block:: python .. code-block:: python
import paddle.profiler as profiler
with profiler.RecordEvent(name='op1', event_type=TracerEventType=TracerEventType.UserDefined): import paddle
op1() import paddle.profiler as profiler
# method1: using context manager
with profiler.RecordEvent("record_add"):
data1 = paddle.randn(shape=[3])
data2 = paddle.randn(shape=[3])
result = data1 + data2
# method2: call begin() and end()
record_event = profiler.RecordEvent("record_add")
record_event.begin()
data1 = paddle.randn(shape=[3])
data2 = paddle.randn(shape=[3])
result = data1 + data2
record_event.end()
Note:
RecordEvent will take effect only when profiler is on and at the state of RECORD.
""" """
def __init__(self, def __init__(self,
...@@ -57,6 +72,20 @@ class RecordEvent(ContextDecorator): ...@@ -57,6 +72,20 @@ class RecordEvent(ContextDecorator):
self.end() self.end()
def begin(self): def begin(self):
r"""
Record the time of begining.
.. code-block:: python
import paddle
import paddle.profiler as profiler
record_event = profiler.RecordEvent("record_sub")
record_event.begin()
data1 = paddle.randn(shape=[3])
data2 = paddle.randn(shape=[3])
result = data1 - data2
record_event.end()
"""
if self.event_type not in _AllowedEventTypeList: if self.event_type not in _AllowedEventTypeList:
warn("Only TracerEvent Type in [{}, {}, {}, {}, {}, {},{}]\ warn("Only TracerEvent Type in [{}, {}, {}, {}, {}, {},{}]\
can be recorded.".format(*_AllowedEventTypeList)) can be recorded.".format(*_AllowedEventTypeList))
...@@ -67,10 +96,51 @@ class RecordEvent(ContextDecorator): ...@@ -67,10 +96,51 @@ class RecordEvent(ContextDecorator):
self.event = _RecordEvent(self.name, self.event_type) self.event = _RecordEvent(self.name, self.event_type)
def end(self): def end(self):
r'''
Record the time of ending.
.. code-block:: python
import paddle
import paddle.profiler as profiler
record_event = profiler.RecordEvent("record_mul")
record_event.begin()
data1 = paddle.randn(shape=[3])
data2 = paddle.randn(shape=[3])
result = data1 * data2
record_event.end()
'''
if self.event: if self.event:
self.event.end() self.event.end()
def load_profiler_result(filename: str):
r"""
Load dumped profiler data back to memory.
Parameters:
filename(str): Name of the exported protobuf file of profiler data.
Returns:
ProfilerResult object.
Examples:
.. code-block:: python
# required: gpu
import paddle.profiler as profiler
with profiler.Profiler(
targets=[profiler.ProfilerTarget.CPU, profiler.ProfilerTarget.GPU],
scheduler = (3, 10)) as p:
for iter in range(10):
#train()
p.step()
p.export('test_export_protobuf.pb', format='pb')
profiler_result = profiler.load_profiler_result('test_export_protobuf.pb')
"""
return core.load_profiler_result(filename)
def wrap_optimizers(): def wrap_optimizers():
def optimizer_warpper(func): def optimizer_warpper(func):
@functools.wraps(func) @functools.wraps(func)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册