tests_perf.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. import sys
  2. from contextlib import contextmanager
  3. from functools import wraps
  4. from time import sleep, time
  5. # Use relative/cpu timer to have reliable timings when there is a sudden load
  6. try:
  7. from time import process_time
  8. except ImportError:
  9. from time import clock
  10. process_time = clock
  11. from tqdm import tqdm, trange
  12. from .tests_tqdm import importorskip, mark, patch_lock, skip
  13. pytestmark = mark.slow
  14. def cpu_sleep(t):
  15. """Sleep the given amount of cpu time"""
  16. start = process_time()
  17. while (process_time() - start) < t:
  18. pass
  19. def checkCpuTime(sleeptime=0.2):
  20. """Check if cpu time works correctly"""
  21. if checkCpuTime.passed:
  22. return True
  23. # First test that sleeping does not consume cputime
  24. start1 = process_time()
  25. sleep(sleeptime)
  26. t1 = process_time() - start1
  27. # secondly check by comparing to cpusleep (where we actually do something)
  28. start2 = process_time()
  29. cpu_sleep(sleeptime)
  30. t2 = process_time() - start2
  31. if abs(t1) < 0.0001 and t1 < t2 / 10:
  32. checkCpuTime.passed = True
  33. return True
  34. skip("cpu time not reliable on this machine")
  35. checkCpuTime.passed = False
  36. @contextmanager
  37. def relative_timer():
  38. """yields a context timer function which stops ticking on exit"""
  39. start = process_time()
  40. def elapser():
  41. return process_time() - start
  42. yield lambda: elapser()
  43. spent = elapser()
  44. def elapser(): # NOQA
  45. return spent
  46. def retry_on_except(n=3, check_cpu_time=True):
  47. """decroator for retrying `n` times before raising Exceptions"""
  48. def wrapper(func):
  49. """actual decorator"""
  50. @wraps(func)
  51. def test_inner(*args, **kwargs):
  52. """may skip if `check_cpu_time` fails"""
  53. for i in range(1, n + 1):
  54. try:
  55. if check_cpu_time:
  56. checkCpuTime()
  57. func(*args, **kwargs)
  58. except Exception:
  59. if i >= n:
  60. raise
  61. else:
  62. return
  63. return test_inner
  64. return wrapper
  65. def simple_progress(iterable=None, total=None, file=sys.stdout, desc='',
  66. leave=False, miniters=1, mininterval=0.1, width=60):
  67. """Simple progress bar reproducing tqdm's major features"""
  68. n = [0] # use a closure
  69. start_t = [time()]
  70. last_n = [0]
  71. last_t = [0]
  72. if iterable is not None:
  73. total = len(iterable)
  74. def format_interval(t):
  75. mins, s = divmod(int(t), 60)
  76. h, m = divmod(mins, 60)
  77. if h:
  78. return '{0:d}:{1:02d}:{2:02d}'.format(h, m, s)
  79. else:
  80. return '{0:02d}:{1:02d}'.format(m, s)
  81. def update_and_print(i=1):
  82. n[0] += i
  83. if (n[0] - last_n[0]) >= miniters:
  84. last_n[0] = n[0]
  85. if (time() - last_t[0]) >= mininterval:
  86. last_t[0] = time() # last_t[0] == current time
  87. spent = last_t[0] - start_t[0]
  88. spent_fmt = format_interval(spent)
  89. rate = n[0] / spent if spent > 0 else 0
  90. rate_fmt = "%.2fs/it" % (1.0 / rate) if 0.0 < rate < 1.0 else "%.2fit/s" % rate
  91. frac = n[0] / total
  92. percentage = int(frac * 100)
  93. eta = (total - n[0]) / rate if rate > 0 else 0
  94. eta_fmt = format_interval(eta)
  95. # full_bar = "#" * int(frac * width)
  96. barfill = " " * int((1.0 - frac) * width)
  97. bar_length, frac_bar_length = divmod(int(frac * width * 10), 10)
  98. full_bar = '#' * bar_length
  99. frac_bar = chr(48 + frac_bar_length) if frac_bar_length else ' '
  100. file.write("\r%s %i%%|%s%s%s| %i/%i [%s<%s, %s]" %
  101. (desc, percentage, full_bar, frac_bar, barfill, n[0],
  102. total, spent_fmt, eta_fmt, rate_fmt))
  103. if n[0] == total and leave:
  104. file.write("\n")
  105. file.flush()
  106. def update_and_yield():
  107. for elt in iterable:
  108. yield elt
  109. update_and_print()
  110. update_and_print(0)
  111. if iterable is not None:
  112. return update_and_yield()
  113. else:
  114. return update_and_print
  115. def assert_performance(thresh, name_left, time_left, name_right, time_right):
  116. """raises if time_left > thresh * time_right"""
  117. if time_left > thresh * time_right:
  118. raise ValueError(
  119. ('{name[0]}: {time[0]:f}, '
  120. '{name[1]}: {time[1]:f}, '
  121. 'ratio {ratio:f} > {thresh:f}').format(
  122. name=(name_left, name_right),
  123. time=(time_left, time_right),
  124. ratio=time_left / time_right, thresh=thresh))
  125. @retry_on_except()
  126. def test_iter_basic_overhead():
  127. """Test overhead of iteration based tqdm"""
  128. total = int(1e6)
  129. a = 0
  130. with trange(total) as t:
  131. with relative_timer() as time_tqdm:
  132. for i in t:
  133. a += i
  134. assert a == (total ** 2 - total) / 2.0
  135. a = 0
  136. with relative_timer() as time_bench:
  137. for i in range(total):
  138. a += i
  139. sys.stdout.write(str(a))
  140. assert_performance(3, 'trange', time_tqdm(), 'range', time_bench())
  141. @retry_on_except()
  142. def test_manual_basic_overhead():
  143. """Test overhead of manual tqdm"""
  144. total = int(1e6)
  145. with tqdm(total=total * 10, leave=True) as t:
  146. a = 0
  147. with relative_timer() as time_tqdm:
  148. for i in range(total):
  149. a += i
  150. t.update(10)
  151. a = 0
  152. with relative_timer() as time_bench:
  153. for i in range(total):
  154. a += i
  155. sys.stdout.write(str(a))
  156. assert_performance(5, 'tqdm', time_tqdm(), 'range', time_bench())
  157. def worker(total, blocking=True):
  158. def incr_bar(x):
  159. for _ in trange(total, lock_args=None if blocking else (False,),
  160. miniters=1, mininterval=0, maxinterval=0):
  161. pass
  162. return x + 1
  163. return incr_bar
  164. @retry_on_except()
  165. @patch_lock(thread=True)
  166. def test_lock_args():
  167. """Test overhead of nonblocking threads"""
  168. ThreadPoolExecutor = importorskip('concurrent.futures').ThreadPoolExecutor
  169. total = 16
  170. subtotal = 10000
  171. with ThreadPoolExecutor() as pool:
  172. sys.stderr.write('block ... ')
  173. sys.stderr.flush()
  174. with relative_timer() as time_tqdm:
  175. res = list(pool.map(worker(subtotal, True), range(total)))
  176. assert sum(res) == sum(range(total)) + total
  177. sys.stderr.write('noblock ... ')
  178. sys.stderr.flush()
  179. with relative_timer() as time_noblock:
  180. res = list(pool.map(worker(subtotal, False), range(total)))
  181. assert sum(res) == sum(range(total)) + total
  182. assert_performance(0.5, 'noblock', time_noblock(), 'tqdm', time_tqdm())
  183. @retry_on_except(10)
  184. def test_iter_overhead_hard():
  185. """Test overhead of iteration based tqdm (hard)"""
  186. total = int(1e5)
  187. a = 0
  188. with trange(total, leave=True, miniters=1,
  189. mininterval=0, maxinterval=0) as t:
  190. with relative_timer() as time_tqdm:
  191. for i in t:
  192. a += i
  193. assert a == (total ** 2 - total) / 2.0
  194. a = 0
  195. with relative_timer() as time_bench:
  196. for i in range(total):
  197. a += i
  198. sys.stdout.write(("%i" % a) * 40)
  199. assert_performance(130, 'trange', time_tqdm(), 'range', time_bench())
  200. @retry_on_except(10)
  201. def test_manual_overhead_hard():
  202. """Test overhead of manual tqdm (hard)"""
  203. total = int(1e5)
  204. with tqdm(total=total * 10, leave=True, miniters=1,
  205. mininterval=0, maxinterval=0) as t:
  206. a = 0
  207. with relative_timer() as time_tqdm:
  208. for i in range(total):
  209. a += i
  210. t.update(10)
  211. a = 0
  212. with relative_timer() as time_bench:
  213. for i in range(total):
  214. a += i
  215. sys.stdout.write(("%i" % a) * 40)
  216. assert_performance(130, 'tqdm', time_tqdm(), 'range', time_bench())
  217. @retry_on_except(10)
  218. def test_iter_overhead_simplebar_hard():
  219. """Test overhead of iteration based tqdm vs simple progress bar (hard)"""
  220. total = int(1e4)
  221. a = 0
  222. with trange(total, leave=True, miniters=1,
  223. mininterval=0, maxinterval=0) as t:
  224. with relative_timer() as time_tqdm:
  225. for i in t:
  226. a += i
  227. assert a == (total ** 2 - total) / 2.0
  228. a = 0
  229. s = simple_progress(range(total), leave=True,
  230. miniters=1, mininterval=0)
  231. with relative_timer() as time_bench:
  232. for i in s:
  233. a += i
  234. assert_performance(10, 'trange', time_tqdm(), 'simple_progress', time_bench())
  235. @retry_on_except(10)
  236. def test_manual_overhead_simplebar_hard():
  237. """Test overhead of manual tqdm vs simple progress bar (hard)"""
  238. total = int(1e4)
  239. with tqdm(total=total * 10, leave=True, miniters=1,
  240. mininterval=0, maxinterval=0) as t:
  241. a = 0
  242. with relative_timer() as time_tqdm:
  243. for i in range(total):
  244. a += i
  245. t.update(10)
  246. simplebar_update = simple_progress(total=total * 10, leave=True,
  247. miniters=1, mininterval=0)
  248. a = 0
  249. with relative_timer() as time_bench:
  250. for i in range(total):
  251. a += i
  252. simplebar_update(10)
  253. assert_performance(10, 'tqdm', time_tqdm(), 'simple_progress', time_bench())