Coverage for python/lsst/pipe/base/timer.py: 23%
Shortcuts 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
Shortcuts 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 logging
28import resource
29import time
30import datetime
33def logPairs(obj, pairs, logLevel=logging.DEBUG, metadata=None, logger=None):
34 """Log ``(name, value)`` pairs to ``obj.metadata`` and ``obj.log``
36 Parameters
37 ----------
38 obj : `lsst.pipe.base.Task`-type
39 A `~lsst.pipe.base.Task` or any other object with these two attributes:
41 - ``metadata`` an instance of `lsst.daf.base.PropertyList`` (or other
42 object with ``add(name, value)`` method).
43 - ``log`` an instance of `logging.Logger` or subclass.
45 If `None`, at least one of ``metadata`` or ``logger`` should be passed
46 or this function will do nothing.
48 pairs : sequence
49 A sequence of ``(name, value)`` pairs, with value typically numeric.
50 logLevel : `int, optional
51 Log level (an `logging` level constant, such as `logging.DEBUG`).
52 metadata : `lsst.daf.base.PropertyList`, optional
53 Metadata object to write entries to. Ignored if `None`.
54 logger : `logging.Logger`
55 Log object to write entries to. Ignored if `None`.
56 """
57 if obj is not None:
58 if metadata is None:
59 metadata = obj.metadata
60 if logger is None:
61 logger = obj.log
62 strList = []
63 for name, value in pairs:
64 if metadata is not None:
65 try:
66 # Use LongLong explicitly here in case an early value in the
67 # sequence is int-sized
68 metadata.addLongLong(name, value)
69 except TypeError:
70 metadata.add(name, value)
71 strList.append(f"{name}={value}")
72 if logger is not None:
73 logging.getLogger("timer." + logger.name).log(logLevel, "; ".join(strList))
76def logInfo(obj, prefix, logLevel=logging.DEBUG, metadata=None, logger=None):
77 """Log timer information to ``obj.metadata`` and ``obj.log``.
79 Parameters
80 ----------
81 obj : `lsst.pipe.base.Task`-type or `None`
82 A `~lsst.pipe.base.Task` or any other object with these two attributes:
84 - ``metadata`` an instance of `lsst.daf.base.PropertyList`` (or other
85 object with ``add(name, value)`` method).
86 - ``log`` an instance of `logging.Logger` or subclass.
88 If `None`, at least one of ``metadata`` or ``logger`` should be passed
89 or this function will do nothing.
91 prefix
92 Name prefix, the resulting entries are ``CpuTime``, etc.. For example
93 timeMethod uses ``prefix = Start`` when the method begins and
94 ``prefix = End`` when the method ends.
95 logLevel : optional
96 Log level (an `logging` level constant, such as `logging.DEBUG`).
97 metadata : `lsst.daf.base.PropertyList`, optional
98 Metadata object to write entries to, overriding ``obj.metadata``.
99 logger : `logging.Logger`
100 Log object to write entries to, overriding ``obj.log``.
102 Notes
103 -----
104 Logged items include:
106 - ``Utc``: UTC date in ISO format (only in metadata since log entries have
107 timestamps).
108 - ``CpuTime``: System + User CPU time (seconds). This should only be used
109 in differential measurements; the time reference point is undefined.
110 - ``MaxRss``: maximum resident set size.
112 All logged resource information is only for the current process; child
113 processes are excluded.
114 """
115 cpuTime = time.process_time()
116 utcStr = datetime.datetime.utcnow().isoformat()
117 res = resource.getrusage(resource.RUSAGE_SELF)
118 if metadata is None and obj is not None:
119 metadata = obj.metadata
120 if metadata is not None:
121 metadata.add(name=prefix + "Utc", value=utcStr) # log messages already have timestamps
122 logPairs(obj=obj,
123 pairs=[
124 (prefix + "CpuTime", cpuTime),
125 (prefix + "UserTime", res.ru_utime),
126 (prefix + "SystemTime", res.ru_stime),
127 (prefix + "MaxResidentSetSize", int(res.ru_maxrss)),
128 (prefix + "MinorPageFaults", int(res.ru_minflt)),
129 (prefix + "MajorPageFaults", int(res.ru_majflt)),
130 (prefix + "BlockInputs", int(res.ru_inblock)),
131 (prefix + "BlockOutputs", int(res.ru_oublock)),
132 (prefix + "VoluntaryContextSwitches", int(res.ru_nvcsw)),
133 (prefix + "InvoluntaryContextSwitches", int(res.ru_nivcsw)),
134 ],
135 logLevel=logLevel,
136 metadata=metadata,
137 logger=logger)
140def timeMethod(func):
141 """Decorator to measure duration of a task method.
143 Parameters
144 ----------
145 func
146 The method to wrap.
148 Notes
149 -----
150 Writes various measures of time and possibly memory usage to the task's
151 metadata; all items are prefixed with the function name.
153 .. warning::
155 This decorator only works with instance methods of Task, or any class
156 with these attributes:
158 - ``metadata``: an instance of `lsst.daf.base.PropertyList` (or other
159 object with ``add(name, value)`` method).
160 - ``log``: an instance of `logging.Logger` or subclass.
162 Examples
163 --------
164 To use:
166 .. code-block:: python
168 import lsst.pipe.base as pipeBase
169 class FooTask(pipeBase.Task):
170 pass
172 @pipeBase.timeMethod
173 def run(self, ...): # or any other instance method you want to time
174 pass
175 """
177 @functools.wraps(func)
178 def wrapper(self, *args, **keyArgs):
179 logInfo(obj=self, prefix=func.__name__ + "Start")
180 try:
181 res = func(self, *args, **keyArgs)
182 finally:
183 logInfo(obj=self, prefix=func.__name__ + "End")
184 return res
185 return wrapper