Coverage for cpyquickhelper/numbers/speed_measure.py: 100%
81 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-30 05:30 +0200
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-30 05:30 +0200
1"""
2@file
3@brief Measures speed.
4"""
5import sys
6from timeit import Timer
9def measure_time(stmt, context=None, repeat=10, number=50, div_by_number=False,
10 max_time=None):
11 """
12 Measures a statement and returns the results as a dictionary.
14 :param stmt: string
15 :param context: variable to know in a dictionary
16 :param repeat: average over *repeat* experiment
17 :param number: number of executions in one row
18 :param div_by_number: divide by the number of executions
19 :param max_time: execute the statement until the total goes
20 beyond this time (approximatively), *repeat* is ignored,
21 *div_by_number* must be set to True
22 :return: dictionary
24 .. runpython::
25 :showcode:
27 from cpyquickhelper.numbers import measure_time
28 from math import cos
30 res = measure_time(lambda: cos(0.5))
31 print(res)
33 See `Timer.repeat <https://docs.python.org/3/library/
34 timeit.html?timeit.Timer.repeat>`_
35 for a better understanding of parameter *repeat* and *number*.
36 The function returns a duration corresponding to
37 *number* times the execution of the main statement.
39 .. versionchanged:: 0.4
40 Parameter *max_time* was added.
41 """
42 if not callable(stmt) and not isinstance(stmt, str):
43 raise TypeError(
44 f"stmt is not callable or a string but is of type {type(stmt)!r}.")
45 if context is None:
46 context = {}
48 import numpy # pylint: disable=C0415
49 if isinstance(stmt, str):
50 tim = Timer(stmt, globals=context)
51 else:
52 tim = Timer(stmt)
54 if max_time is not None:
55 if not div_by_number:
56 raise ValueError(
57 "div_by_number must be set to True of max_time is defined.")
58 i = 1
59 total_time = 0
60 results = []
61 while True:
62 for j in (1, 2):
63 number = i * j
64 time_taken = tim.timeit(number)
65 results.append((number, time_taken))
66 total_time += time_taken
67 if total_time >= max_time:
68 break
69 if total_time >= max_time:
70 break
71 ratio = (max_time - total_time) / total_time
72 ratio = max(ratio, 1)
73 i = int(i * ratio)
75 res = numpy.array(results)
76 tw = res[:, 0].sum()
77 ttime = res[:, 1].sum()
78 mean = ttime / tw
79 ave = res[:, 1] / res[:, 0]
80 dev = (((ave - mean) ** 2 * res[:, 0]).sum() / tw) ** 0.5
81 mes = dict(average=mean, deviation=dev, # pylint: disable=R1735
82 min_exec=numpy.min(ave),
83 max_exec=numpy.max(ave),
84 repeat=1, number=tw,
85 ttime=ttime)
86 else:
87 res = numpy.array(tim.repeat(repeat=repeat, number=number))
88 if div_by_number:
89 res /= number
91 mean = numpy.mean(res)
92 dev = numpy.mean(res ** 2)
93 dev = (dev - mean**2) ** 0.5
94 mes = dict(average=mean, deviation=dev, # pylint: disable=R1735
95 min_exec=numpy.min(res),
96 max_exec=numpy.max(res), repeat=repeat,
97 number=number, ttime=res.sum())
99 if 'values' in context:
100 if hasattr(context['values'], 'shape'):
101 mes['size'] = context['values'].shape[0]
102 else:
103 mes['size'] = len(context['values']) # pragma: no cover
104 else:
105 mes['context_size'] = sys.getsizeof(context)
106 return mes
109def _fcts():
110 """
111 Returns functions to measure.
112 """
113 import numpy # pylint: disable=C0415
114 from .cbenchmark_dot import vector_dot_product # pylint: disable=E0611,C0415
115 from .cbenchmark_dot import vector_dot_product16 # pylint: disable=E0611,C0415
116 from .cbenchmark_dot import vector_dot_product16_nofcall # pylint: disable=E0611,C0415
117 from .cbenchmark_dot import vector_dot_product16_sse # pylint: disable=E0611,C0415
119 def simple_dot(values):
120 return numpy.dot(values, values)
122 def c11_dot(vect):
123 return vector_dot_product(vect, vect)
125 def c11_dot16(vect):
126 return vector_dot_product16(vect, vect)
128 def c11_dot16_nofcall(vect):
129 return vector_dot_product16_nofcall(vect, vect)
131 def c11_dot16_sse(vect):
132 return vector_dot_product16_sse(vect, vect)
134 return [simple_dot, c11_dot, c11_dot16, c11_dot16_nofcall, c11_dot16_sse]
137def check_speed(dims=[100000], repeat=10, number=50, fLOG=print): # pylint: disable=W0102
138 """
139 Prints out some information about speed computation
140 of this laptop. See :ref:`cbenchmarkbranchingrst` to compare.
142 @param dims sets of dimensions to try
143 @param repeat average over *repeat* experiment
144 @param number number of execution in one row
145 @param fLOG logging function
146 @return iterator on results
148 :epkg:`numpy` is multithreaded. For an accurate comparison,
149 this needs to be disabled. This can be done by setting environment variable
150 ``MKL_NUM_THREADS=1`` or by running:
152 ::
154 import mkl
155 mkl.set_num_threads(1)
157 .. index:: MKL_NUM_THREADS
159 One example of use:
161 .. runpython::
162 :showcode:
164 from cpyquickhelper.numbers import check_speed
165 res = list(check_speed(dims=[100, 1000]))
166 import pprint
167 pprint.pprint(res)
168 """
169 import numpy # pylint: disable=C0415
170 fcts = _fcts()
171 mx = max(dims)
172 vect = numpy.ones((mx,))
173 for i in range(0, vect.shape[0]):
174 vect[i] = i
175 for i in dims:
176 values = vect[:i].copy()
177 for fct in fcts:
178 ct = {"fct": fct, 'values': values}
179 t = measure_time(lambda f=fct, v=values: f(v),
180 repeat=repeat, number=number, context=ct)
181 t['name'] = fct.__name__
182 if fLOG:
183 fLOG(t)
184 yield t