|
4 | 4 | import pandas as pd |
5 | 5 | from pathlib import Path |
6 | 6 | import argparse # Add argparse for command line arguments |
| 7 | +import cpuinfo |
7 | 8 |
|
8 | 9 | def parse_benchmark_results(file_path): |
9 | 10 | """Parse benchmark results from file.""" |
@@ -187,60 +188,70 @@ def plot_comparisons(grouped_benchmarks, output_dir): |
187 | 188 | plain_times.append(row['time_ms_plain']) |
188 | 189 | speedups.append(row['speedup_percent']) |
189 | 190 |
|
190 | | - # Sort the data by data type and operation for better visualization |
191 | | - sorted_indices = np.argsort([f"{dt}_{op}" for dt, op in zip(data_types, operations)]) |
| 191 | + # Sort the data by speedup in decreasing order |
| 192 | + speedup_x = [speedup / 100 + 1 for speedup in speedups] |
| 193 | + sorted_indices = np.argsort(speedup_x)[::-1] # Sort in decreasing order |
192 | 194 | categories = [categories[i] for i in sorted_indices] |
193 | 195 | data_types = [data_types[i] for i in sorted_indices] |
194 | 196 | operations = [operations[i] for i in sorted_indices] |
195 | 197 | simd_times = [simd_times[i] for i in sorted_indices] |
196 | 198 | plain_times = [plain_times[i] for i in sorted_indices] |
197 | 199 | speedups = [speedups[i] for i in sorted_indices] |
| 200 | + speedup_x = [speedup_x[i] for i in sorted_indices] |
198 | 201 |
|
199 | 202 | # Create the consolidated comparison plot |
200 | 203 | plt.figure(figsize=(18, 10)) |
201 | 204 | bar_width = 0.35 |
202 | 205 | x = np.arange(len(categories)) |
203 | | - |
204 | | - # Create a bar chart with SIMD and Plain implementations |
205 | | - plt.bar(x - bar_width/2, simd_times, bar_width, label='SIMD', color='royalblue') |
206 | | - plt.bar(x + bar_width/2, plain_times, bar_width, label='Plain', color='lightcoral') |
207 | | - |
208 | | - plt.xlabel('Benchmark Category', fontsize=12) |
209 | | - plt.ylabel('Time (ms)', fontsize=12) |
210 | | - plt.title('SIMD vs Plain Performance Comparison', fontsize=14) |
211 | | - |
212 | | - # Add speedup text on top of bars |
213 | | - for i in range(len(categories)): |
214 | | - speedup = speedups[i] |
215 | | - color = 'green' if speedup > 0 else 'red' |
216 | | - position = max(simd_times[i], plain_times[i]) + 0.002 |
217 | | - plt.text(i, position, f"{speedup:.1f}%", ha='center', color=color, weight='bold') |
218 | | - |
219 | | - plt.xticks(x, categories, rotation=45, ha='right', fontsize=10) |
220 | | - plt.legend(fontsize=12) |
221 | | - plt.tight_layout() |
222 | | - plt.grid(axis='y', linestyle='--', alpha=0.7) |
223 | | - plt.savefig(output_path / "consolidated_comparison.png", dpi=300) |
| 206 | + |
224 | 207 |
|
225 | 208 | # Also create a speedup chart |
226 | 209 | plt.figure(figsize=(18, 10)) |
227 | | - colors = ['green' if s > 0 else 'red' for s in speedups] |
228 | | - plt.bar(x, speedups, color=colors) |
229 | | - plt.axhline(y=0, color='k', linestyle='-', alpha=0.3) |
| 210 | + colors = ['limegreen' if s > 0 else 'red' for s in speedups] |
| 211 | + plt.bar(x, speedup_x, 0.5, color=colors) |
230 | 212 |
|
231 | 213 | plt.xlabel('Benchmark Category', fontsize=12) |
232 | | - plt.ylabel('Speedup (%)', fontsize=12) |
233 | | - plt.title('SIMD Speedup over Plain Implementation', fontsize=14) |
| 214 | + plt.ylabel('Speedup', fontsize=12) |
| 215 | + compiler_text = ("GCC " + gcc_version) if os_platform == 'Linux' else (("MSVC " + msvc_version) if os_platform == 'Windows' else "Unknown Compiler") |
| 216 | + plt.title(f"SIMD Speedup Over Plain Implementation ({os_platform} {compiler_text})", fontsize=14, weight='bold') |
234 | 217 |
|
235 | 218 | # Add speedup values as text |
236 | 219 | for i in range(len(categories)): |
237 | | - va = 'bottom' if speedups[i] > 0 else 'top' |
238 | | - offset = 2 if speedups[i] > 0 else -2 |
239 | | - plt.text(i, speedups[i] + offset, f"{speedups[i]:.1f}%", ha='center', va=va, fontsize=10) |
| 220 | + va = 'bottom' if speedup_x[i] > 1 else 'top' |
| 221 | + offset = 0.05 if speedup_x[i] > 1 else -0.15 |
| 222 | + plt.text(i, speedup_x[i] + offset, f"{speedup_x[i]:.2f}x", ha='center', va=va, fontsize=10, weight='bold') |
| 223 | + |
| 224 | + # Format y-axis ticks to show "x" suffix |
| 225 | + from matplotlib.ticker import FuncFormatter |
| 226 | + def format_speedup(value, pos): |
| 227 | + return f"{value:.0f}x" |
| 228 | + plt.gca().yaxis.set_major_formatter(FuncFormatter(format_speedup)) |
240 | 229 |
|
241 | 230 | plt.xticks(x, categories, rotation=45, ha='right', fontsize=10) |
242 | 231 | plt.tight_layout() |
243 | 232 | plt.grid(axis='y', linestyle='--', alpha=0.7) |
| 233 | + # Get CPU info including cores, architecture and add it to the plot as a box on top right |
| 234 | + cpu_info = cpuinfo.get_cpu_info() |
| 235 | + cpu_name = cpu_info.get('brand_raw', 'N/A') |
| 236 | + cpu_arch = cpu_info.get('arch_string_raw', 'N/A') |
| 237 | + cpu_cores = cpu_info.get('count', 'N/A') |
| 238 | + cpu_freq_actual = cpu_info.get('hz_actual_friendly', 'N/A') |
| 239 | + cpu_freq_advertised = cpu_info.get('hz_advertised_friendly', 'N/A') |
| 240 | + |
| 241 | + info_text = ( |
| 242 | + f"CPU: {cpu_name}\n" |
| 243 | + f"Arch: {cpu_arch}\n" |
| 244 | + f"Cores: {cpu_cores}\n" |
| 245 | + f"Freq (Actual): {cpu_freq_actual}\n" |
| 246 | + f"Freq (Advertised): {cpu_freq_advertised}" |
| 247 | + ) |
| 248 | + |
| 249 | + # Position the text box on the top right |
| 250 | + # Adjust x and y coordinates as needed based on your plot's scale |
| 251 | + # Using axes coordinates (0 to 1 for x and y) for positioning relative to the plot area |
| 252 | + plt.text(0.80, 0.98, info_text, transform=plt.gca().transAxes, |
| 253 | + fontsize=9, verticalalignment='top', horizontalalignment='left', |
| 254 | + bbox=dict(boxstyle='round,pad=0.5', fc='wheat', alpha=0.5)) |
244 | 255 | plt.savefig(output_path / "consolidated_speedup.png", dpi=300) |
245 | 256 |
|
246 | 257 | # Create a table plot with the data |
@@ -303,33 +314,66 @@ def generate_summary_report(grouped_benchmarks, output_dir): |
303 | 314 | data_type = group['data_type'] |
304 | 315 | operation = group['operation'] |
305 | 316 |
|
306 | | - f.write(f"## {data_type} {operation}\n\n") |
307 | | - f.write("| Variant | SIMD Time (ms) | Plain Time (ms) | Speedup (%) |\n") |
| 317 | + f.write(f"#### {data_type} {operation}\n\n") |
| 318 | + f.write("| Variant | SIMD Time (ms) | Plain Time (ms) | Speedup (x) |\n") |
308 | 319 | f.write("|---------|---------------|----------------|------------|\n") |
309 | 320 |
|
310 | 321 | for _, row in comp_df.iterrows(): |
311 | 322 | simd_time = row['time_ms_simd'] |
312 | 323 | plain_time = row['time_ms_plain'] |
313 | 324 | speedup = row['speedup_percent'] |
314 | 325 |
|
315 | | - f.write(f"| {row['size']} | {simd_time:.3f} | {plain_time:.3f} | {speedup:.2f} |\n") |
| 326 | + f.write(f"| {row['size']} | {simd_time:.3f} | {plain_time:.3f} | {speedup/100.0 + 1:.2f}x |\n") |
316 | 327 |
|
317 | 328 | f.write("\n") |
318 | 329 |
|
| 330 | +import platform |
| 331 | +import subprocess |
319 | 332 | def main(): |
| 333 | + global gcc_version |
| 334 | + global msvc_version |
| 335 | + global os_platform |
| 336 | + |
| 337 | + os_platform = platform.system() |
| 338 | + |
| 339 | + if os_platform == 'Linux': |
| 340 | + output = subprocess.check_output(['gcc', '--version'], stderr=subprocess.STDOUT) |
| 341 | + output = output.decode('utf-8') |
| 342 | + gcc_version = re.search(r'(\d+\.\d+\.\d+)', output).group(1) |
| 343 | + print(f"GCC version: {gcc_version}") |
| 344 | + elif os_platform == 'Windows': |
| 345 | + try: |
| 346 | + result = subprocess.run( |
| 347 | + [ |
| 348 | + r"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe", |
| 349 | + "-latest", |
| 350 | + "-products", "*", |
| 351 | + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", |
| 352 | + "-property", "catalog_productDisplayVersion" |
| 353 | + ], |
| 354 | + stdout=subprocess.PIPE, |
| 355 | + stderr=subprocess.PIPE, |
| 356 | + text=True, |
| 357 | + check=True |
| 358 | + ) |
| 359 | + msvc_version = result.stdout.strip() |
| 360 | + except subprocess.CalledProcessError as e: |
| 361 | + print(f"Error getting MSVC version: {e}") |
| 362 | + return None |
| 363 | + |
320 | 364 | """Main function to run the benchmark analysis.""" |
321 | 365 | # Parse command line arguments |
322 | 366 | parser = argparse.ArgumentParser(description='Analyze SIMD benchmark results.') |
323 | | - parser.add_argument('--input', '-i', |
| 367 | + parser.add_argument('--input_file', '-i', |
324 | 368 | required=True, |
325 | 369 | help='Path to the benchmark results file') |
326 | | - parser.add_argument('--output', '-o', |
| 370 | + parser.add_argument('--output_dir', '-o', |
327 | 371 | required=True, |
328 | 372 | help='Directory to save analysis results') |
329 | 373 | args = parser.parse_args() |
330 | 374 |
|
331 | | - input_file = args.input |
332 | | - output_dir = args.output |
| 375 | + input_file = args.input_file |
| 376 | + output_dir = args.output_dir |
333 | 377 |
|
334 | 378 | print(f"Analyzing benchmarks from: {input_file}") |
335 | 379 | print(f"Saving results to: {output_dir}") |
|
0 commit comments