@@ -38,7 +38,7 @@ def parse_report(filepath):
3838
3939 m = re .search (r"\*\*(ALTERs/sec|SELECTs/sec|Operations/sec)\*\*:\s*([\d.]+)" , content )
4040 if m :
41- metrics ["throughput " ] = float (m .group (2 ))
41+ metrics ["throughput_ops " ] = float (m .group (2 ))
4242
4343 for pct in ["p50" , "p90" , "p95" , "p99" ]:
4444 m = re .search (rf"\|\s*{ pct } \s*\|\s*([\d.]+)\s*\|" , content )
@@ -65,6 +65,17 @@ def parse_report(filepath):
6565 if m :
6666 metrics ["columns" ] = int (m .group (1 ))
6767
68+ m = re .search (r"\*\*Tables per iteration\*\*:\s*(\d+)" , content )
69+ if m :
70+ metrics ["tables_per_iteration" ] = int (m .group (1 ))
71+
72+ # Also match older reports that used "Tables": N
73+ if "tables_per_iteration" not in metrics :
74+ m = re .search (r"\*\*Total SELECTs\*\*:\s*(\d+)" , content )
75+ iters = metrics .get ("iterations" , 1 )
76+ if m and iters :
77+ metrics ["tables_per_iteration" ] = int (float (m .group (1 ))) // iters
78+
6879 m = re .search (r"\*\*Tags per ALTER\*\*:\s*(\d+)" , content )
6980 if m :
7081 metrics ["tags" ] = int (m .group (1 ))
@@ -111,17 +122,19 @@ def discover_reports():
111122
112123 threads = metrics ["threads" ]
113124
125+ tbl = metrics .get ("tables_per_iteration" , "?" )
126+
114127 if report_type == "alter" and category == "column" :
115128 cols = metrics .get ("columns" , "?" )
116129 tags = metrics .get ("tags" , "?" )
117- label = f"ALTER column tags (c ={ cols } , t ={ tags } )"
130+ label = f"ALTER column tags (columns ={ cols } , tags_per_column ={ tags } , tables= { tbl } )"
118131 elif report_type == "alter" and category == "table" :
119132 tags = metrics .get ("tags" , "?" )
120- label = f"ALTER table tags (t ={ tags } )"
133+ label = f"ALTER table tags (tags ={ tags } , tables= { tbl } )"
121134 elif report_type == "info_schema" and category == "column" :
122- label = "info_schema column_tags SELECT"
135+ label = f "info_schema column_tags SELECT (tables= { tbl } ) "
123136 elif report_type == "info_schema" and category == "table" :
124- label = "info_schema table_tags SELECT"
137+ label = f "info_schema table_tags SELECT (tables= { tbl } ) "
125138 else :
126139 continue
127140
@@ -130,23 +143,24 @@ def discover_reports():
130143 if existing and metrics .get ("iterations" , 0 ) <= existing .get ("iterations" , 0 ):
131144 continue
132145
146+ # Compute tables/sec from wall-clock and tables_per_iteration
147+ tpi = metrics .get ("tables_per_iteration" )
148+ wc = metrics .get ("wall_clock_s" )
149+ if tpi and wc and wc > 0 :
150+ metrics ["tables_per_sec" ] = round (tpi / wc , 2 )
151+
133152 categories [category ][label ][threads ] = metrics
134153 print (f" [{ category } ] { label } threads={ threads } : "
135154 f"wall={ metrics .get ('wall_clock_s' , '?' )} s, "
136155 f"p50={ metrics .get ('p50' , '?' )} ms, "
137- f"throughput ={ metrics .get ('throughput ' , '?' )} ops/s "
156+ f"tables/s ={ metrics .get ('tables_per_sec ' , '?' )} "
138157 f"[{ fname } ]" )
139158
140159 return categories
141160
142161
143- def plot_category (category_name , series , output_path ):
144- """Generate a 2x2 chart PNG for one category (column or table)."""
145- if not series :
146- print (f" No data for { category_name } , skipping." )
147- return
148-
149- # Color/style assignment
162+ def build_style_map (series ):
163+ """Assign colors and styles to series labels."""
150164 colors_info = ["#d62728" , "#ff7f0e" ]
151165 colors_alter = ["#1f77b4" , "#2ca02c" , "#9467bd" , "#17becf" , "#8c564b" ]
152166 info_idx = 0
@@ -161,16 +175,20 @@ def plot_category(category_name, series, output_path):
161175 style_map [label ] = {"color" : colors_alter [alter_idx % len (colors_alter )], "marker" : "s" , "linestyle" : "-" }
162176 alter_idx += 1
163177
164- fig , axes = plt . subplots ( 2 , 2 , figsize = ( 16 , 12 ))
178+ return style_map
165179
166- chart_configs = [
167- (axes [0 ][0 ], "wall_clock_s" , "Wall-Clock Time (seconds)" , "Wall-Clock Time vs Thread Count" ),
168- (axes [0 ][1 ], "throughput" , "Operations / second" , "Throughput vs Thread Count" ),
169- (axes [1 ][0 ], "p50" , "P50 Latency (ms)" , "P50 Latency vs Thread Count" ),
170- (axes [1 ][1 ], "p99" , "P99 Latency (ms)" , "P99 Latency vs Thread Count" ),
171- ]
172180
173- for ax , metric_key , ylabel , title in chart_configs :
181+ def plot_charts (series , style_map , chart_configs , suptitle , output_path ):
182+ """Generate a chart PNG with len(chart_configs) subplots."""
183+ n = len (chart_configs )
184+ cols = 2
185+ rows = (n + 1 ) // 2
186+ fig , axes = plt .subplots (rows , cols , figsize = (16 , 6 * rows ))
187+ if rows == 1 :
188+ axes = [axes ]
189+
190+ for idx , (metric_key , ylabel , title ) in enumerate (chart_configs ):
191+ ax = axes [idx // cols ][idx % cols ]
174192 for label , thread_data in sorted (series .items ()):
175193 threads = sorted (thread_data .keys ())
176194 values = [thread_data [t ].get (metric_key ) for t in threads ]
@@ -184,15 +202,51 @@ def plot_category(category_name, series, output_path):
184202 ax .legend (fontsize = 8 )
185203 ax .grid (True , alpha = 0.3 )
186204
187- title_label = "Column Tags" if category_name == "column" else "Table Tags"
188- plt .suptitle (f"SET TAGS Profiling: { title_label } — info_schema SELECT vs Direct ALTER" ,
189- fontsize = 14 , fontweight = "bold" )
205+ # Hide unused subplot if odd number of charts
206+ if n % 2 == 1 :
207+ axes [rows - 1 ][1 ].set_visible (False )
208+
209+ plt .suptitle (suptitle , fontsize = 14 , fontweight = "bold" )
190210 plt .tight_layout ()
191211 plt .savefig (output_path , dpi = 150 , bbox_inches = "tight" )
192212 plt .close (fig )
193213 print (f" Chart saved to: { output_path } " )
194214
195215
216+ def plot_category (category_name , series , output_dir ):
217+ """Generate two PNGs per category: table-level comparison + individual operation detail."""
218+ if not series :
219+ print (f" No data for { category_name } , skipping." )
220+ return
221+
222+ style_map = build_style_map (series )
223+ title_label = "Column Tags" if category_name == "column" else "Table Tags"
224+
225+ # Chart 1: Table-level comparison (apples-to-apples across approaches)
226+ table_charts = [
227+ ("wall_clock_s" , "Wall-Clock Time (seconds)" , "Wall-Clock Time vs Thread Count (Lower is better)" ),
228+ ("tables_per_sec" , "Tables / second" , "Tables Processed per Second vs Thread Count (Higher is better)" ),
229+ ]
230+ plot_charts (
231+ series , style_map , table_charts ,
232+ f"{ title_label } : Table-Level Comparison — info_schema SELECT vs Direct ALTER" ,
233+ os .path .join (output_dir , f"comparison_{ category_name } _tags_tables.png" ),
234+ )
235+
236+ # Chart 2: Individual operation detail (per-op latency)
237+ op_charts = [
238+ ("throughput_ops" , "Individual Operations / second" , "Individual Op Throughput vs Thread Count (Higher is better)" ),
239+ ("p50" , "P50 Latency per Op (ms)" , "P50 Latency vs Thread Count (Lower is better)" ),
240+ ("p99" , "P99 Latency per Op (ms)" , "P99 Latency vs Thread Count (Lower is better)" ),
241+ ("max" , "Max Latency per Op (ms)" , "Max Latency vs Thread Count (Lower is better)" ),
242+ ]
243+ plot_charts (
244+ series , style_map , op_charts ,
245+ f"{ title_label } : Individual Operation Detail" ,
246+ os .path .join (output_dir , f"comparison_{ category_name } _tags_ops.png" ),
247+ )
248+
249+
196250if __name__ == "__main__" :
197251 print ("Discovering results...\n " )
198252 categories = discover_reports ()
@@ -202,14 +256,12 @@ def plot_category(category_name, series, output_path):
202256 print (f"\n Found { total_series } series across { total_points } data points.\n " )
203257
204258 if "column" in categories :
205- print ("Generating column tags chart..." )
206- plot_category ("column" , categories ["column" ],
207- os .path .join (RESULTS_DIR , "comparison_column_tags.png" ))
259+ print ("Generating column tags charts..." )
260+ plot_category ("column" , categories ["column" ], RESULTS_DIR )
208261
209262 if "table" in categories :
210- print ("Generating table tags chart..." )
211- plot_category ("table" , categories ["table" ],
212- os .path .join (RESULTS_DIR , "comparison_table_tags.png" ))
263+ print ("Generating table tags charts..." )
264+ plot_category ("table" , categories ["table" ], RESULTS_DIR )
213265
214266 if not categories :
215267 print ("No results found. Run experiments first." )
0 commit comments