11from __future__ import annotations
22
33import argparse
4+ import contextlib
45import os
56import subprocess
67import sys
@@ -27,9 +28,14 @@ def parse_args() -> argparse.Namespace:
2728 "and validate deterministic results."
2829 )
2930 )
30- parser .add_argument ("--target-gb" , type = float , default = 8.0 , help = "Target Arrow table size in GiB." )
3131 parser .add_argument (
32- "--chunk-rows" , type = int , default = 1_000_000 , help = "Rows per generated Arrow record batch."
32+ "--target-gb" , type = float , default = 8.0 , help = "Target Arrow table size in GiB."
33+ )
34+ parser .add_argument (
35+ "--chunk-rows" ,
36+ type = int ,
37+ default = 1_000_000 ,
38+ help = "Rows per generated Arrow record batch." ,
3339 )
3440 parser .add_argument (
3541 "--filter-cutoff" ,
@@ -38,9 +44,14 @@ def parse_args() -> argparse.Namespace:
3844 help = "Filter predicate uses n.filter_key < cutoff where filter_key is in [0, 999]." ,
3945 )
4046 parser .add_argument (
41- "--threads" , type = int , default = max (2 , os .cpu_count () or 2 ), help = "Ladybug query worker threads."
47+ "--threads" ,
48+ type = int ,
49+ default = max (2 , os .cpu_count () or 2 ),
50+ help = "Ladybug query worker threads." ,
51+ )
52+ parser .add_argument (
53+ "--db-path" , type = str , default = "" , help = "Optional database path."
4254 )
43- parser .add_argument ("--db-path" , type = str , default = "" , help = "Optional database path." )
4455 parser .add_argument (
4556 "--query-runs" ,
4657 type = int ,
@@ -51,7 +62,9 @@ def parse_args() -> argparse.Namespace:
5162
5263
5364def read_process_cpu_percent (pid : int ) -> float :
54- output = subprocess .check_output (["ps" , "-o" , "%cpu=" , "-p" , str (pid )], text = True ).strip ()
65+ output = subprocess .check_output (
66+ ["ps" , "-o" , "%cpu=" , "-p" , str (pid )], text = True
67+ ).strip ()
5568 if not output :
5669 return 0.0
5770 return float (output )
@@ -125,17 +138,17 @@ def build_large_arrow_table(
125138 return pa .Table .from_batches (batches ), expected_count , expected_checksum
126139
127140
128- def measure_query_once (conn : lb .Connection , query : str ) -> tuple [float , int , int , float , float ]:
141+ def measure_query_once (
142+ conn : lb .Connection , query : str
143+ ) -> tuple [float , int , int , float , float ]:
129144 samples : list [float ] = []
130145 stop_event = threading .Event ()
131146
132147 def cpu_sampler () -> None :
133148 pid = os .getpid ()
134149 while not stop_event .is_set ():
135- try :
150+ with contextlib . suppress ( Exception ) :
136151 samples .append (read_process_cpu_percent (pid ))
137- except Exception :
138- pass
139152 time .sleep (0.2 )
140153
141154 sampler_thread = threading .Thread (target = cpu_sampler , daemon = True )
@@ -160,11 +173,14 @@ def cpu_sampler() -> None:
160173def main () -> int :
161174 args = parse_args ()
162175 if not (0 < args .filter_cutoff <= 1000 ):
163- raise ValueError ("--filter-cutoff must be in [1, 1000]." )
176+ msg = "--filter-cutoff must be in [1, 1000]."
177+ raise ValueError (msg )
164178 if args .chunk_rows <= 0 :
165- raise ValueError ("--chunk-rows must be positive." )
179+ msg = "--chunk-rows must be positive."
180+ raise ValueError (msg )
166181 if args .query_runs <= 0 :
167- raise ValueError ("--query-runs must be positive." )
182+ msg = "--query-runs must be positive."
183+ raise ValueError (msg )
168184
169185 target_bytes = int (args .target_gb * (1024 ** 3 ))
170186 db_path_value = args .db_path
@@ -174,15 +190,23 @@ def main() -> int:
174190 temp_dir = tempfile .TemporaryDirectory (prefix = "ladybug_arrow_bench_" )
175191 db_path_value = str (Path (temp_dir .name ) / "bench.lbdb" )
176192
177- print (f"Building Arrow table (target ~{ args .target_gb :.2f} GiB)... and { args .threads } query threads" )
193+ print (
194+ f"Building Arrow table (target ~{ args .target_gb :.2f} GiB)... and { args .threads } query threads"
195+ )
178196 build_start = time .perf_counter ()
179197 table , expected_count , expected_checksum = build_large_arrow_table (
180- target_bytes = target_bytes , chunk_rows = args .chunk_rows , filter_cutoff = args .filter_cutoff
198+ target_bytes = target_bytes ,
199+ chunk_rows = args .chunk_rows ,
200+ filter_cutoff = args .filter_cutoff ,
181201 )
182202 build_secs = time .perf_counter () - build_start
183- print (f"Built table with { table .num_rows :,} rows, { table .nbytes / (1024 ** 3 ):.2f} GiB in { build_secs :.2f} s" )
203+ print (
204+ f"Built table with { table .num_rows :,} rows, { table .nbytes / (1024 ** 3 ):.2f} GiB in { build_secs :.2f} s"
205+ )
184206
185- db = lb .Database (database_path = db_path_value , buffer_pool_size = 256 * 1024 * 1024 , read_only = False )
207+ db = lb .Database (
208+ database_path = db_path_value , buffer_pool_size = 256 * 1024 * 1024 , read_only = False
209+ )
186210 conn = lb .Connection (db , num_threads = args .threads )
187211
188212 table_name = "arrow_cpu_bench"
@@ -192,8 +216,7 @@ def main() -> int:
192216 conn .create_arrow_table (table_name , table )
193217 else :
194218 print (f"Creating node table '{ table_name } ' and loading from Arrow..." )
195- conn .execute (
196- f"""
219+ conn .execute (f"""
197220 CREATE NODE TABLE { table_name } (
198221 id INT64,
199222 filter_key INT32,
@@ -211,8 +234,7 @@ def main() -> int:
211234 x11 INT64,
212235 PRIMARY KEY(id)
213236 )
214- """
215- )
237+ """ )
216238 conn .execute (f"COPY { table_name } FROM $df" , {"df" : table })
217239
218240 query = f"""
@@ -236,11 +258,15 @@ def main() -> int:
236258 run_stats : list [tuple [float , float , float ]] = []
237259 for run_idx in range (1 , args .query_runs + 1 ):
238260 print (f"Running CPU-intensive Cypher query (run { run_idx } )..." )
239- elapsed , actual_count , actual_checksum , avg_cpu , max_cpu = measure_query_once (conn , query )
261+ elapsed , actual_count , actual_checksum , avg_cpu , max_cpu = measure_query_once (
262+ conn , query
263+ )
240264 print (f"Query time: { elapsed :.2f} s" )
241265 print (f"CPU usage during query: avg={ avg_cpu :.1f} % max={ max_cpu :.1f} %" )
242266 print (f"Expected cnt={ expected_count :,} , actual cnt={ actual_count :,} " )
243- print (f"Expected checksum={ expected_checksum :,} , actual checksum={ actual_checksum :,} " )
267+ print (
268+ f"Expected checksum={ expected_checksum :,} , actual checksum={ actual_checksum :,} "
269+ )
244270
245271 if actual_count != expected_count or actual_checksum != expected_checksum :
246272 if using_arrow_memory_table :
@@ -250,7 +276,8 @@ def main() -> int:
250276 conn .close ()
251277 if temp_dir :
252278 temp_dir .cleanup ()
253- raise AssertionError ("Query result validation failed." )
279+ msg = "Query result validation failed."
280+ raise AssertionError (msg )
254281
255282 run_stats .append ((elapsed , avg_cpu , max_cpu ))
256283
@@ -261,7 +288,9 @@ def main() -> int:
261288
262289 # >100% indicates more than one core on ps-based accounting.
263290 if max_cpu_overall <= 100.0 :
264- print ("Warning: max CPU did not exceed 100%; try larger target-gb/chunk-rows or more threads." )
291+ print (
292+ "Warning: max CPU did not exceed 100%; try larger target-gb/chunk-rows or more threads."
293+ )
265294 else :
266295 print ("Observed CPU > 100%, indicating multi-core usage." )
267296
0 commit comments