@@ -2545,6 +2545,12 @@ def __init__(self, staticdata, jitdriver_sd, force_finish_trace=False):
25452545
25462546 self .box_names_memo = {}
25472547
2548+ # Batched profiler counting for fast-path operations
2549+ # Instead of calling profiler.count_ops() for every operation,
2550+ # we batch counts and flush them at strategic points (guards, end of trace)
2551+ self ._batched_ops_count = 0
2552+ self ._batched_recorded_ops_count = 0
2553+
25482554 self .aborted_tracing_jitdriver = None
25492555 self .aborted_tracing_greenkey = None
25502556
@@ -2554,6 +2560,30 @@ def __init__(self, staticdata, jitdriver_sd, force_finish_trace=False):
25542560 self .force_finish_trace = force_finish_trace
25552561 self .trace_length_at_last_tco = - 1
25562562
2563+ @always_inline
2564+ def batch_op_count (self ):
2565+ """Batch an operation count instead of calling profiler directly.
2566+
2567+ This is used by fast-path recording methods to avoid the overhead
2568+ of calling profiler.count_ops() for every operation. Counts are
2569+ flushed at strategic points (guards, end of trace).
2570+ """
2571+ self ._batched_ops_count += 1
2572+ self ._batched_recorded_ops_count += 1
2573+
2574+ def flush_batched_counts (self ):
2575+ """Flush batched operation counts to the profiler.
2576+
2577+ Called before guards and at end of trace to ensure accurate counting.
2578+ """
2579+ profiler = self .staticdata .profiler
2580+ if self ._batched_ops_count > 0 :
2581+ profiler .count (Counters .OPS , self ._batched_ops_count )
2582+ self ._batched_ops_count = 0
2583+ if self ._batched_recorded_ops_count > 0 :
2584+ profiler .count (Counters .RECORDED_OPS , self ._batched_recorded_ops_count )
2585+ self ._batched_recorded_ops_count = 0
2586+
25572587 def retrace_needed (self , trace , exported_state ):
25582588 self .partial_trace = trace
25592589 self .retracing_from = self .potential_retrace_position
@@ -2699,6 +2729,8 @@ def check_recursion_invariant(self):
26992729 raise AssertionError
27002730
27012731 def generate_guard (self , opnum , box = None , extraarg = None , resumepc = - 1 ):
2732+ # Flush batched counts before guard to ensure accurate profiling
2733+ self .flush_batched_counts ()
27022734 if isinstance (box , Const ): # no need for a guard
27032735 return
27042736 if opnum == rop .GUARD_EXCEPTION :
@@ -2848,8 +2880,8 @@ def _record_helper(self, opnum, resvalue, descr, *argboxes):
28482880 def _record_int_binop (self , opnum , resvalue , b1 , b2 ):
28492881 if not we_are_translated ():
28502882 PyjitplCounters ._record_int_binop_calls += 1
2851- profiler = self . staticdata . profiler
2852- profiler . count_ops ( opnum , Counters . RECORDED_OPS )
2883+ # Use batched counting instead of calling profiler directly
2884+ self . batch_op_count ( )
28532885 if self .framestack :
28542886 self .framestack [- 1 ].jitcode .traced_operations += 1
28552887 op = self .history .record2_int (opnum , b1 , b2 , resvalue )
@@ -2859,8 +2891,8 @@ def _record_int_binop(self, opnum, resvalue, b1, b2):
28592891 def _record_int_unop (self , opnum , resvalue , b1 ):
28602892 if not we_are_translated ():
28612893 PyjitplCounters ._record_int_binop_calls += 1
2862- profiler = self . staticdata . profiler
2863- profiler . count_ops ( opnum , Counters . RECORDED_OPS )
2894+ # Use batched counting instead of calling profiler directly
2895+ self . batch_op_count ( )
28642896 if self .framestack :
28652897 self .framestack [- 1 ].jitcode .traced_operations += 1
28662898 op = self .history .record1_int (opnum , b1 , resvalue )
@@ -2870,8 +2902,8 @@ def _record_int_unop(self, opnum, resvalue, b1):
28702902 def _record_float_binop (self , opnum , resvalue , b1 , b2 ):
28712903 if not we_are_translated ():
28722904 PyjitplCounters ._record_int_binop_calls += 1
2873- profiler = self . staticdata . profiler
2874- profiler . count_ops ( opnum , Counters . RECORDED_OPS )
2905+ # Use batched counting instead of calling profiler directly
2906+ self . batch_op_count ( )
28752907 if self .framestack :
28762908 self .framestack [- 1 ].jitcode .traced_operations += 1
28772909 op = self .history .record2_float (opnum , b1 , b2 , resvalue )
@@ -3060,6 +3092,7 @@ def compile_and_run_once(self, jitdriver_sd, *args):
30603092 original_boxes = self .initialize_original_boxes (jitdriver_sd , * args )
30613093 return self ._compile_and_run_once (original_boxes )
30623094 finally :
3095+ self .flush_batched_counts () # Flush before end_tracing
30633096 self .staticdata .profiler .end_tracing ()
30643097 debug_stop ('jit-tracing' )
30653098
@@ -3101,6 +3134,7 @@ def handle_guard_failure(self, resumedescr, deadframe):
31013134 self .run_blackhole_interp_to_cancel_tracing (stb )
31023135 finally :
31033136 self .resumekey_original_loop_token = None
3137+ self .flush_batched_counts () # Flush before end_tracing
31043138 self .staticdata .profiler .end_tracing ()
31053139 debug_stop ('jit-tracing' )
31063140
0 commit comments