PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/jit/metainterp/optimizeopt/test/test_costmodel.py

https://bitbucket.org/pypy/pypy/
Python | 274 lines | 250 code | 19 blank | 5 comment | 5 complexity | 3a236a19ba7a56e4c44325d6c8e47554 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import py
  2. from rpython.jit.metainterp.history import TargetToken, JitCellToken, TreeLoop
  3. from rpython.jit.metainterp.optimizeopt.util import equaloplists
  4. from rpython.jit.metainterp.optimizeopt.vector import (Pack, X86_CostModel,
  5. NotAProfitableLoop, VectorizingOptimizer, CostModel)
  6. from rpython.jit.metainterp.optimizeopt.schedule import VecScheduleState
  7. from rpython.jit.metainterp.optimizeopt.dependency import Node, DependencyGraph
  8. from rpython.jit.metainterp.optimizeopt.test.test_util import LLtypeMixin
  9. from rpython.jit.metainterp.optimizeopt.test.test_schedule import SchedulerBaseTest
  10. from rpython.jit.metainterp.optimizeopt.test.test_vecopt import (FakeMetaInterpStaticData,
  11. FakeJitDriverStaticData)
  12. from rpython.jit.metainterp.resoperation import rop, ResOperation, AbstractValue
  13. from rpython.jit.tool.oparser import parse as opparse
  14. from rpython.jit.tool.oparser_model import get_model
  15. from rpython.jit.backend.detect_cpu import getcpuclass
  16. CPU = getcpuclass()
  17. if not CPU.vector_extension:
  18. py.test.skip("this cpu %s has no implemented vector backend" % CPU)
  19. class FakeMemoryRef(object):
  20. def __init__(self, array, iv):
  21. self.index_var = iv
  22. self.array = array
  23. def is_adjacent_after(self, other):
  24. if self.array is not other.array:
  25. return False
  26. iv = self.index_var
  27. ov = other.index_var
  28. val = (int(str(ov.var)[1:]) - int(str(iv.var)[1:]))
  29. # i0 and i1 are adjacent
  30. # i1 and i0 ...
  31. # but not i0, i2
  32. # ...
  33. #print iv, 'is after', ov, "?", val == 1
  34. return val == 1
  35. def prepost_savings(orig_func):
  36. def func(self, *args):
  37. f = getattr(self.proxy, orig_func.__name__)
  38. before_savings = self.proxy.savings
  39. r = f(*args)
  40. after_savings = self.proxy.savings
  41. print " CM %s (%d -> %d, diff: %d) " % (orig_func.__name__,
  42. before_savings, after_savings,
  43. (after_savings - before_savings),)
  44. print " args: ", args
  45. return r
  46. return func
  47. class FakeCostModel(CostModel):
  48. def __init__(self, proxy):
  49. self.proxy = proxy
  50. def getsavings(self):
  51. return self.proxy.savings
  52. @prepost_savings
  53. def reset_savings(self):
  54. raise NotImplementedError
  55. @prepost_savings
  56. def record_cast_int(self, op):
  57. raise NotImplementedError
  58. @prepost_savings
  59. def record_pack_savings(self, pack, times):
  60. raise NotImplementedError
  61. @prepost_savings
  62. def record_vector_pack(self, box, index, count):
  63. raise NotImplementedError
  64. @prepost_savings
  65. def record_vector_unpack(self, box, index, count):
  66. raise NotImplementedError
  67. @prepost_savings
  68. def unpack_cost(self, op, index, count):
  69. raise NotImplementedError
  70. @prepost_savings
  71. def savings_for_pack(self, pack, times):
  72. raise NotImplementedError
  73. def profitable(self):
  74. return self.proxy.savings >= 0
  75. class CostModelBaseTest(SchedulerBaseTest):
  76. def savings(self, loop):
  77. metainterp_sd = FakeMetaInterpStaticData(self.cpu)
  78. jitdriver_sd = FakeJitDriverStaticData()
  79. opt = VectorizingOptimizer(metainterp_sd, jitdriver_sd, 0)
  80. opt.orig_label_args = loop.label.getarglist()[:]
  81. graph = opt.dependency_graph = DependencyGraph(loop)
  82. self.show_dot_graph(graph, 'costmodel')
  83. for k,m in graph.memory_refs.items():
  84. graph.memory_refs[k] = FakeMemoryRef(m.array, m.index_var)
  85. opt.find_adjacent_memory_refs(graph)
  86. opt.extend_packset()
  87. opt.combine_packset()
  88. for pack in opt.packset.packs:
  89. print "pack: \n ",
  90. print '\n '.join([str(op.getoperation()) for op in pack.operations])
  91. print
  92. costmodel = FakeCostModel(X86_CostModel(self.cpu, 0))
  93. costmodel.reset_savings()
  94. state = VecScheduleState(graph, opt.packset, self.cpu, costmodel)
  95. opt.schedule(state)
  96. return costmodel.getsavings()
  97. def assert_operations_match(self, loop_a, loop_b):
  98. assert equaloplists(loop_a.operations, loop_b.operations)
  99. def test_load_2_unpack(self):
  100. loop1 = self.parse_trace("""
  101. f10 = raw_load_f(p0, i0, descr=double)
  102. f11 = raw_load_f(p0, i1, descr=double)
  103. guard_true(i0) [f10]
  104. guard_true(i1) [f11]
  105. """)
  106. # for double the costs are
  107. # unpack index 1 savings: -2
  108. # unpack index 0 savings: -1
  109. savings = self.savings(loop1)
  110. assert savings == -2
  111. def test_load_4_unpack(self):
  112. loop1 = self.parse_trace("""
  113. i10 = raw_load_i(p0, i0, descr=float)
  114. i11 = raw_load_i(p0, i1, descr=float)
  115. i12 = raw_load_i(p0, i2, descr=float)
  116. i13 = raw_load_i(p0, i3, descr=float)
  117. guard_true(i0) [i10]
  118. guard_true(i1) [i11]
  119. guard_true(i2) [i12]
  120. guard_true(i3) [i13]
  121. """)
  122. savings = self.savings(loop1)
  123. assert savings == -1
  124. def test_load_2_unpack_1(self):
  125. loop1 = self.parse_trace("""
  126. f10 = raw_load_f(p0, i0, descr=double)
  127. f11 = raw_load_f(p0, i1, descr=double)
  128. guard_true(i0) [f10]
  129. """)
  130. assert loop1.operations[2].getfailargs()[0] is loop1.operations[0]
  131. savings = self.savings(loop1)
  132. assert savings == 0
  133. assert loop1.operations[2].getfailargs()[0] is loop1.operations[-2]
  134. def test_load_2_unpack_1_index1(self):
  135. loop1 = self.parse_trace("""
  136. f10 = raw_load_f(p0, i0, descr=double)
  137. f11 = raw_load_f(p0, i1, descr=double)
  138. guard_true(i0) [f11]
  139. """)
  140. savings = self.savings(loop1)
  141. assert savings == -1
  142. def test_load_arith1(self):
  143. loop1 = self.parse_trace("""
  144. i10 = raw_load_i(p0, i0, descr=int)
  145. i11 = raw_load_i(p0, i1, descr=int)
  146. i12 = raw_load_i(p0, i2, descr=int)
  147. i13 = raw_load_i(p0, i3, descr=int)
  148. i15 = int_add(i10, 1)
  149. i16 = int_add(i11, 1)
  150. i17 = int_add(i12, 1)
  151. i18 = int_add(i13, 1)
  152. """)
  153. savings = self.savings(loop1)
  154. assert savings == 6
  155. def test_load_arith_store(self):
  156. loop1 = self.parse_trace("""
  157. f10 = raw_load_f(p0, i0, descr=double)
  158. f11 = raw_load_f(p0, i1, descr=double)
  159. i20 = cast_float_to_int(f10)
  160. i21 = cast_float_to_int(f11)
  161. i30 = int_signext(i20, 4)
  162. i31 = int_signext(i21, 4)
  163. raw_store(p0, i3, i30, descr=int)
  164. raw_store(p0, i4, i31, descr=int)
  165. """)
  166. savings = self.savings(loop1)
  167. assert savings >= 0
  168. def test_sum(self):
  169. loop1 = self.parse_trace("""
  170. f10 = raw_load_f(p0, i0, descr=double)
  171. f11 = raw_load_f(p0, i1, descr=double)
  172. f12 = float_add(f1, f10)
  173. f13 = float_add(f12, f11)
  174. """)
  175. savings = self.savings(loop1)
  176. assert savings == 2
  177. @py.test.mark.parametrize("bytes,s", [(1,0),(2,0),(4,0),(8,0)])
  178. def test_sum_float_to_int(self, bytes, s):
  179. loop1 = self.parse_trace("""
  180. f10 = raw_load_f(p0, i0, descr=double)
  181. f11 = raw_load_f(p0, i1, descr=double)
  182. i10 = cast_float_to_int(f10)
  183. i11 = cast_float_to_int(f11)
  184. i12 = int_signext(i10, {c})
  185. i13 = int_signext(i11, {c})
  186. i14 = int_add(i1, i12)
  187. i16 = int_signext(i14, {c})
  188. i15 = int_add(i16, i13)
  189. i17 = int_signext(i15, {c})
  190. """.format(c=bytes))
  191. try:
  192. savings = self.savings(loop1)
  193. if s is None:
  194. py.test.fail("must fail")
  195. # it does not benefit because signext has
  196. # a very inefficient implementation (x86
  197. # does not provide nice instr to convert
  198. # integer sizes)
  199. # signext -> no benefit, + 2x unpack
  200. assert savings <= s
  201. except NotAProfitableLoop:
  202. if s is not None:
  203. py.test.fail("must not fail")
  204. def test_cast(self):
  205. loop1 = self.parse_trace("""
  206. i100 = raw_load_i(p0, i1, descr=float)
  207. i101 = raw_load_i(p0, i2, descr=float)
  208. i102 = raw_load_i(p0, i3, descr=float)
  209. i103 = raw_load_i(p0, i4, descr=float)
  210. #
  211. i104 = raw_load_i(p1, i1, descr=short)
  212. i105 = raw_load_i(p1, i2, descr=short)
  213. i106 = raw_load_i(p1, i3, descr=short)
  214. i107 = raw_load_i(p1, i4, descr=short)
  215. i108 = raw_load_i(p1, i5, descr=short)
  216. i109 = raw_load_i(p1, i6, descr=short)
  217. i110 = raw_load_i(p1, i7, descr=short)
  218. i111 = raw_load_i(p1, i8, descr=short)
  219. #
  220. f100 = cast_int_to_float(i104)
  221. f101 = cast_int_to_float(i105)
  222. f102 = cast_int_to_float(i106)
  223. f103 = cast_int_to_float(i107)
  224. f104 = cast_int_to_float(i108)
  225. f105 = cast_int_to_float(i109)
  226. f106 = cast_int_to_float(i110)
  227. f107 = cast_int_to_float(i111)
  228. """)
  229. try:
  230. self.savings(loop1)
  231. py.test.fail("must not be profitable!")
  232. except NotAProfitableLoop:
  233. pass
  234. def test_force_long_to_int_cast(self):
  235. trace = self.parse_trace("""
  236. i10 = raw_load_i(p0, i1, descr=long)
  237. i11 = raw_load_i(p0, i2, descr=long)
  238. f10 = cast_int_to_float(i10)
  239. f11 = cast_int_to_float(i11)
  240. """)
  241. number = self.savings(trace)
  242. assert number == 1
  243. class Test(CostModelBaseTest, LLtypeMixin):
  244. pass