PageRenderTime 70ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/jit/metainterp/test/test_memmgr.py

https://bitbucket.org/pypy/pypy/
Python | 284 lines | 213 code | 29 blank | 42 comment | 41 complexity | 1f196daf55dee296bb102650b1f0ff72 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys
  2. if len(sys.argv) >= 4 and sys.argv[1] == '--sub':
  3. sys.path[:] = eval(sys.argv[2]) # hacks for test_integration
  4. # XXX we don't invokve py.test machinery but try to make sure
  5. # pypy support code sees the test options from the invoking
  6. # process
  7. import rpython.conftest
  8. class opt:
  9. pass
  10. rpython.conftest.option = opt()
  11. rpython.conftest.option.__dict__.update(eval(sys.argv[3]))
  12. import py
  13. from rpython.jit.metainterp.memmgr import MemoryManager
  14. from rpython.jit.metainterp.test.support import LLJitMixin
  15. from rpython.rlib.jit import JitDriver, dont_look_inside
  16. from rpython.jit.metainterp.warmspot import get_stats
  17. from rpython.jit.metainterp.warmstate import BaseJitCell
  18. from rpython.rlib import rgc
  19. class FakeLoopToken:
  20. generation = 0
  21. invalidated = False
  22. class _TestMemoryManager:
  23. # We spawn a fresh process below to lower the time it takes to do
  24. # all these gc.collect() in memmgr.py. This issue is particularly
  25. # important when running all the tests, because test_[a-l]*.py have
  26. # left tons of stuff in memory. To get temporarily the normal
  27. # behavior just rename this class to TestMemoryManager.
  28. def test_disabled(self):
  29. memmgr = MemoryManager()
  30. memmgr.set_max_age(0)
  31. tokens = [FakeLoopToken() for i in range(10)]
  32. for token in tokens:
  33. memmgr.keep_loop_alive(token)
  34. memmgr.next_generation()
  35. assert memmgr.alive_loops == dict.fromkeys(tokens)
  36. def test_basic(self):
  37. memmgr = MemoryManager()
  38. memmgr.set_max_age(4, 1)
  39. tokens = [FakeLoopToken() for i in range(10)]
  40. for token in tokens:
  41. memmgr.keep_loop_alive(token)
  42. memmgr.next_generation()
  43. assert memmgr.alive_loops == dict.fromkeys(tokens[7:])
  44. def test_basic_2(self):
  45. memmgr = MemoryManager()
  46. memmgr.set_max_age(4, 1)
  47. token = FakeLoopToken()
  48. memmgr.keep_loop_alive(token)
  49. for i in range(10):
  50. memmgr.next_generation()
  51. if i < 3:
  52. assert memmgr.alive_loops == {token: None}
  53. else:
  54. assert memmgr.alive_loops == {}
  55. def test_basic_3(self):
  56. memmgr = MemoryManager()
  57. memmgr.set_max_age(4, 1)
  58. tokens = [FakeLoopToken() for i in range(10)]
  59. for i in range(len(tokens)):
  60. print 'record tokens[%d]' % i
  61. memmgr.keep_loop_alive(tokens[i])
  62. memmgr.next_generation()
  63. for j in range(0, i, 2):
  64. assert tokens[j] in memmgr.alive_loops
  65. print 'also keep alive tokens[%d]' % j
  66. memmgr.keep_loop_alive(tokens[j])
  67. for i in range(len(tokens)):
  68. if i < 7 and (i%2) != 0:
  69. assert tokens[i] not in memmgr.alive_loops
  70. else:
  71. assert tokens[i] in memmgr.alive_loops
  72. class _TestIntegration(LLJitMixin):
  73. # See comments in TestMemoryManager. To get temporarily the normal
  74. # behavior just rename this class to TestIntegration.
  75. # We need an extra rgc.collect in get_procedure_token() for some of
  76. # these tests to pass. But we dont want it there always since that will
  77. # make all other tests take forever.
  78. def setup_class(cls):
  79. original_get_procedure_token = BaseJitCell.get_procedure_token
  80. def get_procedure_token(self):
  81. rgc.collect();
  82. return original_get_procedure_token(self)
  83. BaseJitCell.get_procedure_token = get_procedure_token
  84. cls.original_get_procedure_token = original_get_procedure_token
  85. def teardown_class(cls):
  86. BaseJitCell.get_procedure_token = cls.original_get_procedure_token
  87. def test_loop_kept_alive(self):
  88. myjitdriver = JitDriver(greens=[], reds=['n'])
  89. def g():
  90. n = 10
  91. while n > 0:
  92. myjitdriver.can_enter_jit(n=n)
  93. myjitdriver.jit_merge_point(n=n)
  94. n = n - 1
  95. return 21
  96. def f():
  97. for i in range(15):
  98. g()
  99. return 42
  100. res = self.meta_interp(f, [], loop_longevity=2)
  101. assert res == 42
  102. # we should see only the loop and the entry bridge
  103. self.check_target_token_count(2)
  104. def test_target_loop_kept_alive_or_not(self):
  105. myjitdriver = JitDriver(greens=['m'], reds=['n'])
  106. def g(m):
  107. n = 10
  108. while n > 0:
  109. myjitdriver.can_enter_jit(n=n, m=m)
  110. myjitdriver.jit_merge_point(n=n, m=m)
  111. n = n - 1
  112. return 21
  113. def f():
  114. # If loop_longevity is large enough, this creates a loop
  115. # and an entry bridge for 'g(7)', and another for 'g(5)':
  116. # total 4. If loop_longevity is set to 1 instead, whenever
  117. # we create a loop for 'g(7)' we forget the loop created
  118. # for 'g(5)' and vice-versa. We end up creating loops
  119. # over and over again, for a total of 40 of them.
  120. for i in range(20):
  121. g(7)
  122. g(5)
  123. return 42
  124. # case A
  125. res = self.meta_interp(f, [], loop_longevity=3)
  126. assert res == 42
  127. # we should see (1) the loop-with-preamble, (2) the exit bridge
  128. # for g(7), and another time the same for g(5).
  129. self.check_enter_count(4)
  130. # case B, with a lower longevity
  131. res = self.meta_interp(f, [], loop_longevity=1)
  132. assert res == 42
  133. # we should see a loop for each call to g()
  134. self.check_enter_count(40)
  135. def test_target_loop_kept_alive_or_not_2(self):
  136. myjitdriver = JitDriver(greens=['m'], reds=['n'])
  137. def g(m):
  138. n = 10
  139. while n > 0:
  140. myjitdriver.can_enter_jit(n=n, m=m)
  141. myjitdriver.jit_merge_point(n=n, m=m)
  142. n = n - 1
  143. return 21
  144. def f():
  145. # If loop_longevity is large enough, this creates a loop
  146. # and an entry bridge for 'g(7)', and another for 'g(5)':
  147. # total 4. If loop_longevity is set to 1 instead, whenever
  148. # we create a loop for 'g(7)', we create the entry bridge
  149. # on the next 'g(7)', but we forget them both when we move
  150. # on to 'g(5)', and vice-versa. We end up creating loops
  151. # and entry bridges over and over again, for a total of 32
  152. # of them.
  153. for i in range(8):
  154. g(7); g(7)
  155. g(5); g(5)
  156. return 42
  157. # case A
  158. res = self.meta_interp(f, [], loop_longevity=5)
  159. assert res == 42
  160. # we should see (1) the loop-with-preamble, (2) the exit bridge
  161. # for g(7), and another time the same for g(5).
  162. self.check_enter_count(4)
  163. # case B, with a lower longevity
  164. res = self.meta_interp(f, [], loop_longevity=1)
  165. assert res == 42
  166. # we should see a loop for each call to g()
  167. self.check_enter_count(32)
  168. def test_throw_away_old_loops(self):
  169. myjitdriver = JitDriver(greens=['m'], reds=['n'])
  170. def g(m):
  171. n = 10
  172. while n > 0:
  173. myjitdriver.can_enter_jit(n=n, m=m)
  174. myjitdriver.jit_merge_point(n=n, m=m)
  175. n = n - 1
  176. return 21
  177. def f():
  178. for i in range(10):
  179. g(1) # g(1) gets a loop with an entry bridge
  180. g(2) # and an exit bridge, stays alive
  181. g(1)
  182. g(3)
  183. g(1)
  184. g(4) # g(2), g(3), g(4), g(5) are thrown away every iteration
  185. g(1) # (no entry bridge for them)
  186. g(5)
  187. return 42
  188. res = self.meta_interp(f, [], loop_longevity=3)
  189. assert res == 42
  190. self.check_enter_count(2 + 10*4)
  191. def test_call_assembler_keep_alive(self):
  192. myjitdriver1 = JitDriver(greens=['m'], reds=['n'])
  193. myjitdriver2 = JitDriver(greens=['m'], reds=['n', 'rec'])
  194. def h(m, n):
  195. while True:
  196. myjitdriver1.can_enter_jit(n=n, m=m)
  197. myjitdriver1.jit_merge_point(n=n, m=m)
  198. n = n >> 1
  199. if n == 0:
  200. return 21
  201. def g(m, rec):
  202. n = 5
  203. while n > 0:
  204. myjitdriver2.can_enter_jit(n=n, m=m, rec=rec)
  205. myjitdriver2.jit_merge_point(n=n, m=m, rec=rec)
  206. if rec:
  207. h(m, rec)
  208. n = n - 1
  209. return 21
  210. def f(u):
  211. for i in range(8):
  212. h(u, 32) # make a loop and an exit bridge for h(u)
  213. g(u, 8) # make a loop for g(u) with a call_assembler
  214. g(u, 0); g(u+1, 0) # \
  215. g(u, 0); g(u+2, 0) # \ make more loops for g(u+1) to g(u+4),
  216. g(u, 0); g(u+3, 0) # / but keeps g(u) alive
  217. g(u, 0); g(u+4, 0) # /
  218. g(u, 8) # call g(u) again, with its call_assembler to h(u)
  219. return 42
  220. res = self.meta_interp(f, [1], loop_longevity=4, inline=True)
  221. assert res == 42
  222. self.check_jitcell_token_count(6)
  223. tokens = [t() for t in get_stats().jitcell_token_wrefs]
  224. # Some loops have been freed
  225. assert None in tokens
  226. # Loop with number 1, h(), has not been freed
  227. assert 1 in [t.number for t in tokens if t]
  228. # ____________________________________________________________
  229. def test_all():
  230. if sys.platform == 'win32':
  231. py.test.skip(
  232. "passing repr() to subprocess.Popen probably doesn't work")
  233. import os, subprocess
  234. from rpython.conftest import option
  235. thisfile = os.path.abspath(__file__)
  236. p = subprocess.Popen([sys.executable, thisfile,
  237. '--sub', repr(sys.path), repr(option.__dict__)])
  238. result = p.wait()
  239. assert result == 0
  240. if __name__ == '__main__':
  241. # occurs in the subprocess
  242. for test in [_TestMemoryManager(), _TestIntegration()]:
  243. if hasattr(test, 'setup_class'):
  244. test.setup_class()
  245. try:
  246. for name in dir(test):
  247. if name.startswith('test_'):
  248. print
  249. print '-'*79
  250. print '----- Now running test', name, '-----'
  251. print
  252. getattr(test, name)()
  253. finally:
  254. if hasattr(test, 'teardown_class'):
  255. test.teardown_class()