1+ import sys
12
23from threading import Thread
34from threading import RLock
45from fastrlock .rlock import FastRLock as FLock
56
7+ # Benchmark functions:
8+
9+ cython_code = []
610
711def lock_unlock (l ):
812 l .acquire ()
@@ -17,6 +21,21 @@ def lock_unlock(l):
1721 l .release ()
1822
1923
24+ cython_code .append ("""
25+ def lock_unlock(lock):
26+ lock_fastrlock(lock, -1, True)
27+ unlock_fastrlock(lock)
28+ lock_fastrlock(lock, -1, True)
29+ unlock_fastrlock(lock)
30+ lock_fastrlock(lock, -1, True)
31+ unlock_fastrlock(lock)
32+ lock_fastrlock(lock, -1, True)
33+ unlock_fastrlock(lock)
34+ lock_fastrlock(lock, -1, True)
35+ unlock_fastrlock(lock)
36+ """ )
37+
38+
2039def reentrant_lock_unlock (l ):
2140 l .acquire ()
2241 l .acquire ()
@@ -30,6 +49,21 @@ def reentrant_lock_unlock(l):
3049 l .release ()
3150
3251
52+ cython_code .append ("""
53+ def reentrant_lock_unlock(lock):
54+ lock_fastrlock(lock, -1, True)
55+ lock_fastrlock(lock, -1, True)
56+ lock_fastrlock(lock, -1, True)
57+ lock_fastrlock(lock, -1, True)
58+ lock_fastrlock(lock, -1, True)
59+ unlock_fastrlock(lock)
60+ unlock_fastrlock(lock)
61+ unlock_fastrlock(lock)
62+ unlock_fastrlock(lock)
63+ unlock_fastrlock(lock)
64+ """ )
65+
66+
3367def mixed_lock_unlock (l ):
3468 l .acquire ()
3569 l .release ()
@@ -43,6 +77,21 @@ def mixed_lock_unlock(l):
4377 l .release ()
4478
4579
80+ cython_code .append ("""
81+ def mixed_lock_unlock(lock):
82+ lock_fastrlock(lock, -1, True)
83+ unlock_fastrlock(lock)
84+ lock_fastrlock(lock, -1, True)
85+ lock_fastrlock(lock, -1, True)
86+ unlock_fastrlock(lock)
87+ lock_fastrlock(lock, -1, True)
88+ unlock_fastrlock(lock)
89+ lock_fastrlock(lock, -1, True)
90+ unlock_fastrlock(lock)
91+ unlock_fastrlock(lock)
92+ """ )
93+
94+
4695def context_manager (l ):
4796 with l : pass
4897 with l :
@@ -77,6 +126,24 @@ def lock_unlock_nonblocking(l):
77126 l .release ()
78127
79128
129+ cython_code .append ("""
130+ def lock_unlock_nonblocking(lock):
131+ if lock_fastrlock(lock, -1, False):
132+ unlock_fastrlock(lock)
133+ if lock_fastrlock(lock, -1, False):
134+ unlock_fastrlock(lock)
135+ if lock_fastrlock(lock, -1, False):
136+ unlock_fastrlock(lock)
137+ if lock_fastrlock(lock, -1, False):
138+ unlock_fastrlock(lock)
139+ if lock_fastrlock(lock, -1, False):
140+ unlock_fastrlock(lock)
141+ """ )
142+
143+
144+ # End of benchmark functions
145+
146+
80147def threaded (l , test_func , tcount = 10 ):
81148 threads = [ Thread (target = test_func , args = (l ,)) for _ in range (tcount ) ]
82149 for thread in threads :
@@ -85,7 +152,39 @@ def threaded(l, test_func, tcount=10):
85152 thread .join ()
86153
87154
88- if __name__ == '__main__' :
155+ def run_benchmark (name , lock , version , functions , repeat_count , repeat_count_t ):
156+ print ('Testing %s (%s)' % (name , version ))
157+
158+ from timeit import Timer
159+ from functools import partial
160+
161+ print ("sequential (x%d):" % repeat_count )
162+ for function in functions :
163+ timer = Timer (partial (function , lock ))
164+ print ('%-25s: %9.2f msec' % (function .__name__ , max (timer .repeat (repeat = 4 , number = repeat_count )) * 1000.0 ))
165+
166+ print ("threaded 10T (x%d):" % repeat_count_t )
167+ for function in functions :
168+ timer = Timer (partial (threaded , lock , function ))
169+ print ('%-25s: %9.2f msec' % (function .__name__ , max (timer .repeat (repeat = 4 , number = repeat_count_t )) * 1000.0 ))
170+
171+
172+ if sys .version_info < (3 , 5 ):
173+ import imp
174+
175+ def load_dynamic (name , module_path ):
176+ return imp .load_dynamic (name , module_path )
177+ else :
178+ import importlib .util as _importlib_util
179+
180+ def load_dynamic (name , module_path ):
181+ spec = _importlib_util .spec_from_file_location (name , module_path )
182+ module = _importlib_util .module_from_spec (spec )
183+ spec .loader .exec_module (module )
184+ return module
185+
186+
187+ def main ():
89188 functions = [
90189 lock_unlock ,
91190 reentrant_lock_unlock ,
@@ -95,35 +194,78 @@ def threaded(l, test_func, tcount=10):
95194 ]
96195
97196 import fastrlock
98- import sys
99- from timeit import Timer
100- from functools import partial
197+
198+ import glob
199+ import os .path
200+ import re
201+ import tempfile
101202
102203 repeat_count = 100000
103204 repeat_count_t = 1000
104205
105- rlock , flock = ('threading.RLock' , RLock (), "%d.%d.%d" % sys .version_info [:3 ]), ('FastRLock' , FLock (), fastrlock .__version__ )
206+ rlock = (RLock (), "%d.%d.%d" % sys .version_info [:3 ])
207+ flock = (FLock (), fastrlock .__version__ )
208+
106209 locks = []
107210 args = sys .argv [1 :]
108211 if 'rlock' in args :
109212 locks .append (rlock )
110213 if 'flock' in args :
111214 locks .append (flock )
112- if not locks :
215+ if not args :
113216 locks = [rlock , flock ]
217+
114218 for _ in range (args .count ('quick' )):
115219 repeat_count = max (10 , repeat_count // 100 )
116220 repeat_count_t = max (5 , repeat_count_t // 10 )
117221
118- for name , lock , version in locks :
119- print ('Testing %s (%s)' % (name , version ))
222+ for lock , version in locks :
223+ name = type (lock ).__name__
224+ run_benchmark (name , lock , version , functions , repeat_count , repeat_count_t )
225+
226+ if 'cython' in args or not args :
227+ lock , version = flock
120228
121- print ("sequential (x%d):" % repeat_count )
122- for function in functions :
123- timer = Timer (partial (function , lock ))
124- print ('%-25s: %9.2f msec' % (function .__name__ , max (timer .repeat (repeat = 4 , number = repeat_count )) * 1000.0 ))
229+ from Cython .Build .Cythonize import cython_compile , parse_args
125230
126- print ("threaded 10T (x%d):" % repeat_count_t )
127- for function in functions :
128- timer = Timer (partial (threaded , lock , function ))
129- print ('%-25s: %9.2f msec' % (function .__name__ , max (timer .repeat (repeat = 4 , number = repeat_count_t )) * 1000.0 ))
231+ basepath = None
232+ try :
233+ with tempfile .NamedTemporaryFile (mode = "w" , suffix = '.pyx' ) as f :
234+ code = '\n ' .join (cython_code )
235+ cy_function_names = re .findall (r"def (\w+)\(" , code )
236+ f .write ("from fastrlock.rlock cimport lock_fastrlock, unlock_fastrlock\n \n " )
237+ f .write (code )
238+ f .flush ()
239+
240+ options , _ = parse_args (["" , f .name , "-3" , "--force" , "--inplace" ])
241+ cython_compile (f .name , options )
242+
243+ basepath = os .path .splitext (f .name )[0 ]
244+ for ext in [".*.so" , ".*.pyd" , ".so" , ".pyd" ]:
245+ so_paths = glob .glob (basepath + ext )
246+ if so_paths :
247+ so_path = so_paths [0 ]
248+ break
249+ else :
250+ print ("Failed to find Cython compiled module" )
251+ sys .exit (1 )
252+
253+ module = load_dynamic (os .path .basename (basepath ), so_path )
254+
255+ cy_functions = [getattr (module , name ) for name in cy_function_names ]
256+
257+ run_benchmark ("Cython interface of %s" % type (lock ).__name__ , lock , version ,
258+ cy_functions , repeat_count , repeat_count_t )
259+
260+ finally :
261+ if basepath :
262+ files = glob .glob (basepath )
263+ if len (files ) > 3 :
264+ print ("Found too many artefacts, not deleting temporary files" )
265+ else :
266+ for filename in files :
267+ os .unlink (filename )
268+
269+
270+ if __name__ == '__main__' :
271+ main ()
0 commit comments