Coverage for python/lsst/pipe/base/timer.py : 23%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#
2# LSST Data Management System
3# Copyright 2008, 2009, 2010, 2011 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <http://www.lsstcorp.org/LegalNotices/>.
21#
22"""Utilities for measuring execution time.
23"""
24__all__ = ["logInfo", "timeMethod"]
26import functools
27import resource
28import time
29import datetime
31from lsst.log import Log, log
34def logPairs(obj, pairs, logLevel=Log.DEBUG, metadata=None, logger=None):
35 """Log ``(name, value)`` pairs to ``obj.metadata`` and ``obj.log``
37 Parameters
38 ----------
39 obj : `lsst.pipe.base.Task`-type
40 A `~lsst.pipe.base.Task` or any other object with these two attributes:
42 - ``metadata`` an instance of `lsst.daf.base.PropertyList`` (or other
43 object with ``add(name, value)`` method).
44 - ``log`` an instance of `lsst.log.Log`.
46 If `None`, at least one of ``metadata`` or ``logger`` should be passed
47 or this function will do nothing.
49 pairs : sequence
50 A sequence of ``(name, value)`` pairs, with value typically numeric.
51 logLevel : optional
52 Log level (an `lsst.log` level constant, such as `lsst.log.Log.DEBUG`).
53 metadata : `lsst.daf.base.PropertyList`, optional
54 Metadata object to write entries to. Ignored if `None`.
55 logger : `lsst.log.Log`
56 Log object to write entries to. Ignored if `None`.
57 """
58 if obj is not None:
59 if metadata is None:
60 metadata = obj.metadata
61 if logger is None:
62 logger = obj.log
63 strList = []
64 for name, value in pairs:
65 if metadata is not None:
66 try:
67 # Use LongLong explicitly here in case an early value in the
68 # sequence is int-sized
69 metadata.addLongLong(name, value)
70 except TypeError:
71 metadata.add(name, value)
72 strList.append(f"{name}={value}")
73 if logger is not None:
74 log("timer." + logger.getName(), logLevel, "; ".join(strList))
77def logInfo(obj, prefix, logLevel=Log.DEBUG, metadata=None, logger=None):
78 """Log timer information to ``obj.metadata`` and ``obj.log``.
80 Parameters
81 ----------
82 obj : `lsst.pipe.base.Task`-type or `None`
83 A `~lsst.pipe.base.Task` or any other object with these two attributes:
85 - ``metadata`` an instance of `lsst.daf.base.PropertyList`` (or other
86 object with ``add(name, value)`` method).
87 - ``log`` an instance of `lsst.log.Log`.
89 If `None`, at least one of ``metadata`` or ``logger`` should be passed
90 or this function will do nothing.
92 prefix
93 Name prefix, the resulting entries are ``CpuTime``, etc.. For example
94 timeMethod uses ``prefix = Start`` when the method begins and
95 ``prefix = End`` when the method ends.
96 logLevel : optional
97 Log level (an `lsst.log` level constant, such as `lsst.log.Log.DEBUG`).
98 metadata : `lsst.daf.base.PropertyList`, optional
99 Metadata object to write entries to, overriding ``obj.metadata``.
100 logger : `lsst.log.Log`
101 Log object to write entries to, overriding ``obj.log``.
103 Notes
104 -----
105 Logged items include:
107 - ``Utc``: UTC date in ISO format (only in metadata since log entries have
108 timestamps).
109 - ``CpuTime``: System + User CPU time (seconds). This should only be used
110 in differential measurements; the time reference point is undefined.
111 - ``MaxRss``: maximum resident set size.
113 All logged resource information is only for the current process; child
114 processes are excluded.
115 """
116 cpuTime = time.process_time()
117 utcStr = datetime.datetime.utcnow().isoformat()
118 res = resource.getrusage(resource.RUSAGE_SELF)
119 if metadata is None and obj is not None:
120 metadata = obj.metadata
121 if metadata is not None:
122 metadata.add(name=prefix + "Utc", value=utcStr) # log messages already have timestamps
123 logPairs(obj=obj,
124 pairs=[
125 (prefix + "CpuTime", cpuTime),
126 (prefix + "UserTime", res.ru_utime),
127 (prefix + "SystemTime", res.ru_stime),
128 (prefix + "MaxResidentSetSize", int(res.ru_maxrss)),
129 (prefix + "MinorPageFaults", int(res.ru_minflt)),
130 (prefix + "MajorPageFaults", int(res.ru_majflt)),
131 (prefix + "BlockInputs", int(res.ru_inblock)),
132 (prefix + "BlockOutputs", int(res.ru_oublock)),
133 (prefix + "VoluntaryContextSwitches", int(res.ru_nvcsw)),
134 (prefix + "InvoluntaryContextSwitches", int(res.ru_nivcsw)),
135 ],
136 logLevel=logLevel,
137 metadata=metadata,
138 logger=logger)
141def timeMethod(func):
142 """Decorator to measure duration of a task method.
144 Parameters
145 ----------
146 func
147 The method to wrap.
149 Notes
150 -----
151 Writes various measures of time and possibly memory usage to the task's
152 metadata; all items are prefixed with the function name.
154 .. warning::
156 This decorator only works with instance methods of Task, or any class
157 with these attributes:
159 - ``metadata``: an instance of `lsst.daf.base.PropertyList` (or other
160 object with ``add(name, value)`` method).
161 - ``log``: an instance of `lsst.log.Log`.
163 Examples
164 --------
165 To use:
167 .. code-block:: python
169 import lsst.pipe.base as pipeBase
170 class FooTask(pipeBase.Task):
171 pass
173 @pipeBase.timeMethod
174 def run(self, ...): # or any other instance method you want to time
175 pass
176 """
178 @functools.wraps(func)
179 def wrapper(self, *args, **keyArgs):
180 logInfo(obj=self, prefix=func.__name__ + "Start")
181 try:
182 res = func(self, *args, **keyArgs)
183 finally:
184 logInfo(obj=self, prefix=func.__name__ + "End")
185 return res
186 return wrapper