mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	GH-126892: Reset warmup counters when JIT compiling code (GH-126893)
This commit is contained in:
		
							parent
							
								
									addb225f38
								
							
						
					
					
						commit
						48c50ff1a2
					
				
					 6 changed files with 48 additions and 39 deletions
				
			
		|  | @ -91,7 +91,10 @@ def _make_error(reason, details): | |||
|         res, cmd_line = run_python_until_end( | ||||
|             "-c", | ||||
|             textwrap.dedent(code), | ||||
|             __run_using_command=[_strace_binary] + strace_flags) | ||||
|             __run_using_command=[_strace_binary] + strace_flags, | ||||
|             # Don't want to trace our JIT's own mmap and mprotect calls: | ||||
|             PYTHON_JIT="0", | ||||
|         ) | ||||
|     except OSError as err: | ||||
|         return _make_error("Caught OSError", err) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1385,20 +1385,17 @@ class Foo: | |||
|         guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION") | ||||
|         self.assertEqual(guard_type_version_count, 1) | ||||
| 
 | ||||
|     def test_guard_type_version_not_removed(self): | ||||
|         """ | ||||
|         Verify that the guard type version is not removed if we modify the class | ||||
|         """ | ||||
|     def test_guard_type_version_removed_invalidation(self): | ||||
| 
 | ||||
|         def thing(a): | ||||
|             x = 0 | ||||
|             for i in range(TIER2_THRESHOLD + 100): | ||||
|             for i in range(TIER2_THRESHOLD * 2 + 1): | ||||
|                 x += a.attr | ||||
|                 # for the first (TIER2_THRESHOLD + 90) iterations we set the attribute on this dummy function which shouldn't | ||||
|                 # trigger the type watcher | ||||
|                 # then after for the next 10 it should trigger it and stop optimizing | ||||
|                 # Note that the code needs to be in this weird form so it's optimized inline without any control flow | ||||
|                 setattr((Foo, Bar)[i < TIER2_THRESHOLD + 90], "attr", 2) | ||||
|                 # The first TIER2_THRESHOLD iterations we set the attribute on | ||||
|                 # this dummy class, which shouldn't trigger the type watcher. | ||||
|                 # Note that the code needs to be in this weird form so it's | ||||
|                 # optimized inline without any control flow: | ||||
|                 setattr((Bar, Foo)[i == TIER2_THRESHOLD + 1], "attr", 2) | ||||
|                 x += a.attr | ||||
|             return x | ||||
| 
 | ||||
|  | @ -1410,24 +1407,21 @@ class Bar: | |||
| 
 | ||||
|         res, ex = self._run_with_optimizer(thing, Foo()) | ||||
|         opnames = list(iter_opnames(ex)) | ||||
| 
 | ||||
|         self.assertIsNotNone(ex) | ||||
|         self.assertEqual(res, (TIER2_THRESHOLD * 2) + 219) | ||||
|         guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION") | ||||
|         self.assertEqual(guard_type_version_count, 2) | ||||
|         self.assertEqual(res, TIER2_THRESHOLD * 6 + 1) | ||||
|         call = opnames.index("_CALL_BUILTIN_FAST") | ||||
|         load_attr_top = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", 0, call) | ||||
|         load_attr_bottom = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", call) | ||||
|         self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1) | ||||
|         self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 1) | ||||
| 
 | ||||
| 
 | ||||
|     @unittest.expectedFailure | ||||
|     def test_guard_type_version_not_removed_escaping(self): | ||||
|         """ | ||||
|         Verify that the guard type version is not removed if have an escaping function | ||||
|         """ | ||||
|     def test_guard_type_version_removed_escaping(self): | ||||
| 
 | ||||
|         def thing(a): | ||||
|             x = 0 | ||||
|             for i in range(100): | ||||
|             for i in range(TIER2_THRESHOLD): | ||||
|                 x += a.attr | ||||
|                 # eval should be escaping and so should cause optimization to stop and preserve both type versions | ||||
|                 # eval should be escaping | ||||
|                 eval("None") | ||||
|                 x += a.attr | ||||
|             return x | ||||
|  | @ -1437,12 +1431,12 @@ class Foo: | |||
|         res, ex = self._run_with_optimizer(thing, Foo()) | ||||
|         opnames = list(iter_opnames(ex)) | ||||
|         self.assertIsNotNone(ex) | ||||
|         self.assertEqual(res, 200) | ||||
|         guard_type_version_count = opnames.count("_GUARD_TYPE_VERSION") | ||||
|         # Note: This will actually be 1 for noe | ||||
|         # https://github.com/python/cpython/pull/119365#discussion_r1626220129 | ||||
|         self.assertEqual(guard_type_version_count, 2) | ||||
| 
 | ||||
|         self.assertEqual(res, TIER2_THRESHOLD * 2) | ||||
|         call = opnames.index("_CALL_BUILTIN_FAST_WITH_KEYWORDS") | ||||
|         load_attr_top = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", 0, call) | ||||
|         load_attr_bottom = opnames.index("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", call) | ||||
|         self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1) | ||||
|         self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 1) | ||||
| 
 | ||||
|     def test_guard_type_version_executor_invalidated(self): | ||||
|         """ | ||||
|  |  | |||
|  | @ -0,0 +1,2 @@ | |||
| Require cold or invalidated code to "warm up" before being JIT compiled | ||||
| again. | ||||
|  | @ -2624,15 +2624,16 @@ dummy_func( | |||
|                 } | ||||
|                 _PyExecutorObject *executor; | ||||
|                 int optimized = _PyOptimizer_Optimize(frame, start, stack_pointer, &executor, 0); | ||||
|                 ERROR_IF(optimized < 0, error); | ||||
|                 if (optimized) { | ||||
|                 if (optimized <= 0) { | ||||
|                     this_instr[1].counter = restart_backoff_counter(counter); | ||||
|                     ERROR_IF(optimized < 0, error); | ||||
|                 } | ||||
|                 else { | ||||
|                     this_instr[1].counter = initial_jump_backoff_counter(); | ||||
|                     assert(tstate->previous_executor == NULL); | ||||
|                     tstate->previous_executor = Py_None; | ||||
|                     GOTO_TIER_TWO(executor); | ||||
|                 } | ||||
|                 else { | ||||
|                     this_instr[1].counter = restart_backoff_counter(counter); | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); | ||||
|  | @ -4875,6 +4876,9 @@ dummy_func( | |||
|                         tstate->previous_executor = (PyObject *)current_executor; | ||||
|                         GOTO_TIER_ONE(target); | ||||
|                     } | ||||
|                     else { | ||||
|                         exit->temperature = initial_temperature_backoff_counter(); | ||||
|                     } | ||||
|                 } | ||||
|                 exit->executor = executor; | ||||
|             } | ||||
|  |  | |||
							
								
								
									
										3
									
								
								Python/executor_cases.c.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								Python/executor_cases.c.h
									
										
									
										generated
									
									
									
								
							|  | @ -5750,6 +5750,9 @@ | |||
|                         tstate->previous_executor = (PyObject *)current_executor; | ||||
|                         GOTO_TIER_ONE(target); | ||||
|                     } | ||||
|                     else { | ||||
|                         exit->temperature = initial_temperature_backoff_counter(); | ||||
|                     } | ||||
|                 } | ||||
|                 exit->executor = executor; | ||||
|             } | ||||
|  |  | |||
							
								
								
									
										13
									
								
								Python/generated_cases.c.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								Python/generated_cases.c.h
									
										
									
										generated
									
									
									
								
							|  | @ -5092,15 +5092,18 @@ | |||
|                     _PyFrame_SetStackPointer(frame, stack_pointer); | ||||
|                     int optimized = _PyOptimizer_Optimize(frame, start, stack_pointer, &executor, 0); | ||||
|                     stack_pointer = _PyFrame_GetStackPointer(frame); | ||||
|                     if (optimized < 0) goto error; | ||||
|                     if (optimized) { | ||||
|                     if (optimized <= 0) { | ||||
|                         this_instr[1].counter = restart_backoff_counter(counter); | ||||
|                         if (optimized < 0) goto error; | ||||
|                     } | ||||
|                     else { | ||||
|                         _PyFrame_SetStackPointer(frame, stack_pointer); | ||||
|                         this_instr[1].counter = initial_jump_backoff_counter(); | ||||
|                         stack_pointer = _PyFrame_GetStackPointer(frame); | ||||
|                         assert(tstate->previous_executor == NULL); | ||||
|                         tstate->previous_executor = Py_None; | ||||
|                         GOTO_TIER_TWO(executor); | ||||
|                     } | ||||
|                     else { | ||||
|                         this_instr[1].counter = restart_backoff_counter(counter); | ||||
|                     } | ||||
|                 } | ||||
|                 else { | ||||
|                     ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Brandt Bucher
						Brandt Bucher