1010import os
1111import logging
1212
13+ def get_rss_mb ():
14+ """Gets current Resident Set Size (physical memory) in MB. Linux only."""
15+ try :
16+ with open ('/proc/self/statm' , 'r' ) as f :
17+ rss_pages = int (f .read ().split ()[1 ])
18+ page_size = os .sysconf ('SC_PAGE_SIZE' )
19+ return (rss_pages * page_size ) / (1024 * 1024 )
20+ except Exception :
21+ return 0.0
22+
1323def run_worker (target_module ):
1424 """Performs ONE import and returns metrics."""
1525 tracemalloc .start ()
@@ -19,6 +29,7 @@ def run_worker(target_module):
1929 # This explicitly isolates the pure import latency and entirely omits the
2030 # 10ms-50ms Python VM interpreter startup overhead that would skew the metrics.
2131 start_time = time .perf_counter ()
32+ rss_before = get_rss_mb ()
2233
2334 modules_before = set (sys .modules .keys ())
2435
@@ -27,6 +38,7 @@ def run_worker(target_module):
2738 # ---------------------
2839
2940 end_time = time .perf_counter ()
41+ rss_after = get_rss_mb ()
3042 _ , peak = tracemalloc .get_traced_memory ()
3143 tracemalloc .stop ()
3244
@@ -54,6 +66,7 @@ def run_worker(target_module):
5466 metrics = {
5567 "time_ms" : (end_time - start_time ) * 1000 ,
5668 "peak_ram_mb" : peak / (1024 * 1024 ),
69+ "rss_ram_mb" : rss_after - rss_before ,
5770 "loaded_modules" : len (new_modules ),
5871 "loaded_lines" : loaded_lines
5972 }
@@ -70,7 +83,7 @@ def _run_worker_and_parse(cmd):
7083 break
7184 if data is None :
7285 raise ValueError ("Worker did not output metrics JSON." )
73- for key in ("time_ms" , "peak_ram_mb" , "loaded_modules" , "loaded_lines" ):
86+ for key in ("time_ms" , "peak_ram_mb" , "rss_ram_mb" , " loaded_modules" , "loaded_lines" ):
7487 if key not in data :
7588 raise KeyError (f"Missing key '{ key } ' in worker output" )
7689 return data
@@ -84,7 +97,7 @@ def run_master(iterations, target_module, cpu="0", csv_path=None):
8497 """Orchestrates the benchmark."""
8598 if iterations < 1 :
8699 raise ValueError ("Number of iterations must be at least 1." )
87- times , memories = [], []
100+ times , memories , rss_memories = [], [], []
88101 loaded_modules_val , loaded_lines_val = 0 , 0
89102
90103 print (f"Profiling start... Running { iterations } cold-start iterations for { target_module } ." )
@@ -105,6 +118,7 @@ def run_master(iterations, target_module, cpu="0", csv_path=None):
105118 data = _run_worker_and_parse (cmd )
106119 times .append (data ["time_ms" ])
107120 memories .append (data ["peak_ram_mb" ])
121+ rss_memories .append (data ["rss_ram_mb" ])
108122 loaded_modules_val = data ["loaded_modules" ]
109123 loaded_lines_val = data ["loaded_lines" ]
110124 except FileNotFoundError as e :
@@ -116,6 +130,7 @@ def run_master(iterations, target_module, cpu="0", csv_path=None):
116130 data = _run_worker_and_parse (cmd )
117131 times .append (data ["time_ms" ])
118132 memories .append (data ["peak_ram_mb" ])
133+ rss_memories .append (data ["rss_ram_mb" ])
119134 loaded_modules_val = data ["loaded_modules" ]
120135 loaded_lines_val = data ["loaded_lines" ]
121136 except subprocess .CalledProcessError as err :
@@ -131,9 +146,9 @@ def run_master(iterations, target_module, cpu="0", csv_path=None):
131146 if csv_path :
132147 with open (csv_path , "w" , newline = "" , encoding = "utf-8" ) as f :
133148 writer = csv .writer (f )
134- writer .writerow (["Iteration" , "Time (ms)" , "Peak RAM (MB)" ])
135- for idx , (t , m ) in enumerate (zip (times , memories )):
136- writer .writerow ([idx + 1 , f"{ t :.2f} " , f"{ m :.4f} " ])
149+ writer .writerow (["Iteration" , "Time (ms)" , "Tracemalloc RAM (MB)" , "RSS Physical RAM (MB)" ])
150+ for idx , (t , m , r ) in enumerate (zip (times , memories , rss_memories )):
151+ writer .writerow ([idx + 1 , f"{ t :.2f} " , f"{ m :.4f} " , f" { r :.4f } " ])
137152 print (f"Raw metrics successfully exported to CSV: { csv_path } " )
138153
139154 # Compute percentiles (P50, P90, P99)
@@ -150,6 +165,12 @@ def run_master(iterations, target_module, cpu="0", csv_path=None):
150165 else :
151166 p50_mem = p90_mem = p99_mem = memories [0 ] if memories else 0.0
152167
168+ if len (rss_memories ) > 1 :
169+ q_rss = statistics .quantiles (rss_memories , n = 100 )
170+ p50_rss , p90_rss , p99_rss = q_rss [49 ], q_rss [89 ], q_rss [98 ]
171+ else :
172+ p50_rss = p90_rss = p99_rss = rss_memories [0 ] if rss_memories else 0.0
173+
153174 print (f"\n --- Results for { target_module } ({ iterations } iterations) ---" )
154175 print (f"Code Volume (Deterministic):" )
155176 print (f" Loaded Modules: { loaded_modules_val } " )
@@ -164,7 +185,7 @@ def run_master(iterations, target_module, cpu="0", csv_path=None):
164185 if len (times ) > 1 :
165186 print (f" StdDev: { statistics .stdev (times ):.2f} " )
166187
167- print (f"RAM (MB):" )
188+ print (f"Tracemalloc RAM (MB):" )
168189 print (f" P50 (Median): { p50_mem :.4f} " )
169190 print (f" P90: { p90_mem :.4f} " )
170191 print (f" P99: { p99_mem :.4f} " )
@@ -174,6 +195,16 @@ def run_master(iterations, target_module, cpu="0", csv_path=None):
174195 if len (memories ) > 1 :
175196 print (f" StdDev: { statistics .stdev (memories ):.4f} " )
176197
198+ print (f"Physical RSS RAM (MB):" )
199+ print (f" P50 (Median): { p50_rss :.4f} " )
200+ print (f" P90: { p90_rss :.4f} " )
201+ print (f" P99: { p99_rss :.4f} " )
202+ print (f" Mean: { statistics .mean (rss_memories ):.4f} " )
203+ print (f" Min: { min (rss_memories ):.4f} " )
204+ print (f" Max: { max (rss_memories ):.4f} " )
205+ if len (rss_memories ) > 1 :
206+ print (f" StdDev: { statistics .stdev (rss_memories ):.4f} " )
207+
177208def run_trace (target_module ):
178209 """Generates importtime trace log and writes it to a file."""
179210 trace_file = f"import_trace_{ target_module .replace ('.' , '_' )} .log"
0 commit comments