88
99import argparse
1010import asyncio
11- import sys
1211import statistics
12+ import sys
1313from pathlib import Path
1414
1515# Add project root to path for pyisolate imports
1616project_root = Path (__file__ ).parent .parent
1717sys .path .insert (0 , str (project_root ))
1818
19- from benchmark_harness import BenchmarkHarness
20- from pyisolate import ProxiedSingleton , ExtensionBase , ExtensionConfig , local_execution
19+ from benchmark_harness import BenchmarkHarness # noqa: E402
20+
21+ from pyisolate import ExtensionBase , ExtensionConfig , ProxiedSingleton # noqa: E402
2122
2223try :
23- import torch
24- TORCH_AVAILABLE = True
25- except ImportError :
24+ from importlib .util import find_spec
25+
26+ TORCH_AVAILABLE = find_spec ("torch" ) is not None
27+ except ImportError : # pragma: no cover
2628 TORCH_AVAILABLE = False
27-
29+
2830try :
2931 from tabulate import tabulate
32+
3033 TABULATE_AVAILABLE = True
3134except ImportError :
3235 TABULATE_AVAILABLE = False
3639# Host-side Classes
3740# =============================================================================
3841
42+
3943class DatabaseSingleton (ProxiedSingleton ):
4044 """Simple dictionary-based singleton for testing state."""
45+
4146 def __init__ (self ):
4247 self ._db = {}
4348
@@ -52,11 +57,12 @@ class BenchmarkExtensionWrapper(ExtensionBase):
5257 """
5358 Host-side wrapper that proxies calls to the isolated extension.
5459 """
60+
5561 async def on_module_loaded (self , module ):
5662 """Called when the isolated module is loaded."""
5763 if not getattr (module , "benchmark_entrypoint" , None ):
5864 raise RuntimeError (f"Module { module .__name__ } missing 'benchmark_entrypoint'" )
59-
65+
6066 # Instantiate the child-side extension object
6167 self .extension = module .benchmark_entrypoint ()
6268 await self .extension .initialize ()
@@ -89,7 +95,7 @@ async def get_value(self, key): pass
8995
9096class BenchmarkExtension:
9197 """Child-side extension implementation."""
92-
98+
9399 async def initialize(self):
94100 pass
95101
@@ -179,30 +185,29 @@ def __init__(self, mean, stdev, min_time, max_time):
179185
180186class SimpleRunner :
181187 """Minimal runner to replace TestRPCBenchmarks.runner."""
188+
182189 def __init__ (self , warmup_runs = 5 , benchmark_runs = 1000 ):
183190 self .warmup_runs = warmup_runs
184191 self .benchmark_runs = benchmark_runs
185192
186193 async def run_benchmark (self , name , func ):
187194 import time
195+
188196 times = []
189-
197+
190198 # Warmup
191199 for _ in range (self .warmup_runs ):
192200 await func ()
193-
201+
194202 # Benchmark
195203 for _ in range (self .benchmark_runs ):
196204 start = time .perf_counter ()
197205 await func ()
198206 end = time .perf_counter ()
199207 times .append (end - start )
200-
208+
201209 return BenchmarkResult (
202- statistics .mean (times ),
203- statistics .stdev (times ) if len (times ) > 1 else 0 ,
204- min (times ),
205- max (times )
210+ statistics .mean (times ), statistics .stdev (times ) if len (times ) > 1 else 0 , min (times ), max (times )
206211 )
207212
208213
@@ -211,13 +216,13 @@ async def run_benchmarks(
211216):
212217 print ("PyIsolate RPC Benchmark Suite (Refactored for 1.0)" )
213218 print ("=" * 60 )
214-
219+
215220 harness = BenchmarkHarness ()
216221 await harness .setup_test_environment ("benchmark" )
217-
222+
218223 runner = SimpleRunner (
219- warmup_runs = 2 if quick else 5 ,
220- benchmark_runs = 100 if quick else 1000
224+ warmup_runs = 2 if quick else 5 ,
225+ benchmark_runs = 100 if quick else 1000 ,
221226 )
222227
223228 try :
@@ -230,7 +235,7 @@ async def run_benchmarks(
230235 "benchmark_ext" ,
231236 dependencies = ["numpy>=1.26.0" , "torch>=2.0.0" ] if torch_available else ["numpy>=1.26.0" ],
232237 share_torch = False ,
233- extension_code = BENCHMARK_EXTENSION_CODE
238+ extension_code = BENCHMARK_EXTENSION_CODE ,
234239 )
235240 extensions_config .append ({"name" : "benchmark_ext" , "share" : False })
236241
@@ -239,67 +244,70 @@ async def run_benchmarks(
239244 "benchmark_ext_shared" ,
240245 dependencies = ["numpy>=1.26.0" , "torch>=2.0.0" ],
241246 share_torch = True ,
242- extension_code = BENCHMARK_EXTENSION_CODE
247+ extension_code = BENCHMARK_EXTENSION_CODE ,
243248 )
244249 extensions_config .append ({"name" : "benchmark_ext_shared" , "share" : True })
245250
246251 # Load Extensions using Manager
247252 manager = harness .get_manager (BenchmarkExtensionWrapper )
248-
253+
249254 ext_standard = None
250255 ext_shared = None
251-
256+
252257 for cfg in extensions_config :
253258 name = cfg ["name" ]
254259 share_torch = cfg ["share" ]
255260 print (f"Loading extension { name } (share_torch={ share_torch } )..." )
256-
261+
257262 # Reconstruct minimal deps for config (manager uses this for venv check/install)
258263 deps = ["numpy>=1.26.0" ]
259- if torch_available : deps .append ("torch>=2.0.0" )
260-
264+ if torch_available :
265+ deps .append ("torch>=2.0.0" )
266+
261267 config = ExtensionConfig (
262268 name = name ,
263269 module_path = str (harness .test_root / "extensions" / name ),
264270 isolated = True ,
265271 dependencies = deps ,
266- apis = [DatabaseSingleton ], # Host must allow the singleton
267- share_torch = share_torch
272+ apis = [DatabaseSingleton ], # Host must allow the singleton
273+ share_torch = share_torch ,
268274 )
269-
275+
270276 ext = manager .load_extension (config )
271277 if name == "benchmark_ext" :
272278 ext_standard = ext
273279 else :
274280 ext_shared = ext
275281
276282 print ("Extensions loaded.\n " )
277-
283+
278284 # Define Test Data
279285 test_data = [
280286 ("small_int" , 42 ),
281287 ("small_string" , "hello world" ),
282288 ]
283-
289+
284290 runner_results = {}
285-
291+
286292 # --- Run Benchmarks ---
287293 # Note: In a full implementation, we'd replicate the comprehensive test suite.
288294 # Here we verify core functionality by running the 'do_stuff' generic method.
289295 # This confirms RPC, Serialization, and Process Isolation are working.
290-
296+
291297 target_extensions = []
292- if ext_standard : target_extensions .append (("Standard" , ext_standard ))
293- if ext_shared : target_extensions .append (("Shared" , ext_shared ))
294-
295- for name , ext in target_extensions :
296- print (f"--- Benchmarking { name } Mode ---" )
298+ if ext_standard :
299+ target_extensions .append (("Standard" , ext_standard ))
300+ if ext_shared :
301+ target_extensions .append (("Shared" , ext_shared ))
302+
303+ for mode_name , ext in target_extensions :
304+ print (f"--- Benchmarking { mode_name } Mode ---" )
297305 for data_name , data_val in test_data :
298- bench_name = f"{ name } _{ data_name } "
299-
300- async def func ():
301- return await ext .do_stuff (data_val )
302-
306+ bench_name = f"{ mode_name } _{ data_name } "
307+
308+ async def func (bound_ext = ext , bound_value = data_val ):
309+ return await bound_ext .do_stuff (bound_value )
310+
303311 print (f"Running { bench_name } ..." )
304312 try :
305313 res = await runner .run_benchmark (bench_name , func )
@@ -311,12 +319,12 @@ async def func():
311319 print ("\n " + "=" * 60 )
312320 print ("RESULTS" )
313321 print ("=" * 60 )
314-
322+
315323 headers = ["Test" , "Mean (ms)" , "Std Dev (ms)" ]
316324 table_data = []
317325 for name , res in runner_results .items ():
318- table_data .append ([name , f"{ res .mean * 1000 :.3f} " , f"{ res .stdev * 1000 :.3f} " ])
319-
326+ table_data .append ([name , f"{ res .mean * 1000 :.3f} " , f"{ res .stdev * 1000 :.3f} " ])
327+
320328 if TABULATE_AVAILABLE :
321329 print (tabulate (table_data , headers = headers ))
322330 else :
@@ -325,27 +333,26 @@ async def func():
325333
326334 finally :
327335 await harness .cleanup ()
328-
336+
329337 return 0
330338
331339
332- def main ():
340+ def main () -> int :
333341 parser = argparse .ArgumentParser (description = "PyIsolate 1.0 Benchmark" )
334342 parser .add_argument ("--quick" , action = "store_true" )
335343 parser .add_argument ("--no-torch" , action = "store_true" )
336344 parser .add_argument ("--no-gpu" , action = "store_true" )
337345 parser .add_argument ("--torch-mode" , default = "both" )
338-
346+
339347 args = parser .parse_args ()
340-
341- try :
342- import numpy
343- import psutil
344- except ImportError :
348+
349+ if find_spec ("numpy" ) is None or find_spec ("psutil" ) is None :
345350 print ("Please install dependencies: pip install numpy psutil tabulate" )
346351 return 1
347-
352+
348353 asyncio .run (run_benchmarks (args .quick , args .no_torch , args .no_gpu , args .torch_mode ))
354+ return 0
355+
349356
350357if __name__ == "__main__" :
351- main ()
358+ raise SystemExit ( main () )
0 commit comments