File size: 5,246 Bytes
8207382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# copyright (c) 2021 PaddlePaddle Authors. All Rights Reserve.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
import paddle.profiler as profiler

# A global variable to record the number of calling times for profiler
# functions. It is used to specify the tracing range of training steps.
_profiler_step_id = 0

# A global variable to avoid parsing from string every time.
_profiler_options = None
_prof = None


class ProfilerOptions(object):
    """
    Use a string to initialize a ProfilerOptions.
    The string should be in the format: "key1=value1;key2=value;key3=value3".
    For example:
      "profile_path=model.profile"
      "batch_range=[50, 60]; profile_path=model.profile"
      "batch_range=[50, 60]; tracer_option=OpDetail; profile_path=model.profile"

    ProfilerOptions supports following key-value pair:
      batch_range      - a integer list, e.g. [100, 110].
      state            - a string, the optional values are 'CPU', 'GPU' or 'All'.
      sorted_key       - a string, the optional values are 'calls', 'total',
                         'max', 'min' or 'ave.
      tracer_option    - a string, the optional values are 'Default', 'OpDetail',
                         'AllOpDetail'.
      profile_path     - a string, the path to save the serialized profile data,
                         which can be used to generate a timeline.
      exit_on_finished - a boolean.
    """

    def __init__(self, options_str):
        assert isinstance(options_str, str)

        self._options = {
            "batch_range": [10, 20],
            "state": "All",
            "sorted_key": "total",
            "tracer_option": "Default",
            "profile_path": "/tmp/profile",
            "exit_on_finished": True,
            "timer_only": True,
        }
        self._parse_from_string(options_str)

    def _parse_from_string(self, options_str):
        for kv in options_str.replace(" ", "").split(";"):
            key, value = kv.split("=")
            if key == "batch_range":
                value_list = value.replace("[", "").replace("]", "").split(",")
                value_list = list(map(int, value_list))
                if (
                    len(value_list) >= 2
                    and value_list[0] >= 0
                    and value_list[1] > value_list[0]
                ):
                    self._options[key] = value_list
            elif key == "exit_on_finished":
                self._options[key] = value.lower() in ("yes", "true", "t", "1")
            elif key in ["state", "sorted_key", "tracer_option", "profile_path"]:
                self._options[key] = value
            elif key == "timer_only":
                self._options[key] = value

    def __getitem__(self, name):
        if self._options.get(name, None) is None:
            raise ValueError("ProfilerOptions does not have an option named %s." % name)
        return self._options[name]


def add_profiler_step(options_str=None):
    """
    Enable the operator-level timing using PaddlePaddle's profiler.
    The profiler uses a independent variable to count the profiler steps.
    One call of this function is treated as a profiler step.
    Args:
      profiler_options - a string to initialize the ProfilerOptions.
                         Default is None, and the profiler is disabled.
    """
    if options_str is None:
        return

    global _prof
    global _profiler_step_id
    global _profiler_options

    if _profiler_options is None:
        _profiler_options = ProfilerOptions(options_str)
    # profile : https://www.paddlepaddle.org.cn/documentation/docs/zh/guides/performance_improving/profiling_model.html#chakanxingnengshujudetongjibiaodan
    # timer_only = True  only the model's throughput and time overhead are displayed
    # timer_only = False calling summary can print a statistical form that presents performance data from different perspectives.
    # timer_only = False the output Timeline information can be found in the profiler_log directory
    if _prof is None:
        _timer_only = str(_profiler_options["timer_only"]) == str(True)
        _prof = profiler.Profiler(
            scheduler=(
                _profiler_options["batch_range"][0],
                _profiler_options["batch_range"][1],
            ),
            on_trace_ready=profiler.export_chrome_tracing("./profiler_log"),
            timer_only=_timer_only,
        )
        _prof.start()
    else:
        _prof.step()

    if _profiler_step_id == _profiler_options["batch_range"][1]:
        _prof.stop()
        _prof.summary(op_detail=True, thread_sep=False, time_unit="ms")
        _prof = None
        if _profiler_options["exit_on_finished"]:
            sys.exit(0)

    _profiler_step_id += 1