|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +Python/NumPy Performance Benchmarks |
| 4 | +Generated from Compute Engine expressions |
| 5 | +
|
| 6 | +This script benchmarks NumPy-compiled mathematical expressions |
| 7 | +and compares performance with pure Python evaluation. |
| 8 | +
|
| 9 | +Run with: python benchmarks/python-performance.py |
| 10 | +
|
| 11 | +Requirements: |
| 12 | + pip install numpy |
| 13 | +""" |
| 14 | + |
| 15 | +import numpy as np |
| 16 | +import time |
| 17 | +from typing import Dict, Any, Callable |
| 18 | + |
| 19 | +def benchmark(fn: Callable, iterations: int, **kwargs) -> float: |
| 20 | + """Benchmark a function over multiple iterations""" |
| 21 | + start = time.perf_counter() |
| 22 | + for _ in range(iterations): |
| 23 | + fn(**kwargs) |
| 24 | + end = time.perf_counter() |
| 25 | + return (end - start) * 1000 # Convert to milliseconds |
| 26 | + |
| 27 | +# Generated benchmark functions |
| 28 | + |
| 29 | +def simple_power(x, y, z): |
| 30 | + r"""Simple Power: x^2 + y^2 + z^2""" |
| 31 | + return x ** 2 + y ** 2 + z ** 2 |
| 32 | + |
| 33 | + |
| 34 | +def polynomial(x): |
| 35 | + r"""Polynomial: x^4 + 3x^3 + 2x^2 + x + 1""" |
| 36 | + return x ** 4 + 3 * x ** 3 + 2 * x ** 2 + x + 1 |
| 37 | + |
| 38 | + |
| 39 | +def trigonometric(x, y, z): |
| 40 | + r"""Trigonometric: \sin(x) + \cos(y) + \tan(z)""" |
| 41 | + return np.sin(x) + np.cos(y) + np.tan(z) |
| 42 | + |
| 43 | + |
| 44 | +def nested_expression(x, y, z, a, b, c): |
| 45 | + r"""Nested Expression: \sqrt{(x-a)^2 + (y-b)^2 + (z-c)^2}""" |
| 46 | + return np.sqrt((-a + x) ** 2 + (-b + y) ** 2 + (-c + z) ** 2) |
| 47 | + |
| 48 | + |
| 49 | +def large_expression__50_terms_(x): |
| 50 | + r"""Large Expression (50 terms): x^0 + x^1 + x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9 + x^10 + x^11 + x^12 + x^13 + x^14 + x^15 + x^16 + x^17 + x^18 + x^19 + x^20 + x^21 + x^22 + x^23 + x^24 + x^25 + x^26 + x^27 + x^28 + x^29 + x^30 + x^31 + x^32 + x^33 + x^34 + x^35 + x^36 + x^37 + x^38 + x^39 + x^40 + x^41 + x^42 + x^43 + x^44 + x^45 + x^46 + x^47 + x^48 + x^49""" |
| 51 | + return x ** 9 + x ** 8 + x ** 7 + x ** 6 + x ** 5 + 0 * x ** 4 + 2 * x ** 4 + 3 * x ** 4 + 4 * x ** 4 + 5 * x ** 4 + 6 * x ** 4 + 7 * x ** 4 + 8 * x ** 4 + 9 * x ** 4 + x ** 4 + x ** 4 + 0 * x ** 3 + 2 * x ** 3 + 3 * x ** 3 + 4 * x ** 3 + 5 * x ** 3 + 6 * x ** 3 + 7 * x ** 3 + 8 * x ** 3 + 9 * x ** 3 + x ** 3 + x ** 3 + 0 * x ** 2 + 2 * x ** 2 + 3 * x ** 2 + 4 * x ** 2 + 5 * x ** 2 + 6 * x ** 2 + 7 * x ** 2 + 8 * x ** 2 + 9 * x ** 2 + x ** 2 + x ** 2 + x + x + 0 * x + 2 * x + 3 * x + 4 * x + 5 * x + 6 * x + 7 * x + 8 * x + 9 * x + x ** 0 |
| 52 | + |
| 53 | + |
| 54 | +def many_variables__20_vars_(x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8, x_9, x_10, x_11, x_12, x_13, x_14, x_15, x_16, x_17, x_18, x_19): |
| 55 | + r"""Many Variables (20 vars): x_{0} + x_{1} + x_{2} + x_{3} + x_{4} + x_{5} + x_{6} + x_{7} + x_{8} + x_{9} + x_{10} + x_{11} + x_{12} + x_{13} + x_{14} + x_{15} + x_{16} + x_{17} + x_{18} + x_{19}""" |
| 56 | + return x_0 + x_1 + x_10 + x_11 + x_12 + x_13 + x_14 + x_15 + x_16 + x_17 + x_18 + x_19 + x_2 + x_3 + x_4 + x_5 + x_6 + x_7 + x_8 + x_9 |
| 57 | + |
| 58 | + |
| 59 | +def distance_formula(x_1, y_1, x_2, y_2): |
| 60 | + r"""Distance Formula: \sqrt{(x_2-x_1)^2 + (y_2-y_1)^2}""" |
| 61 | + return np.sqrt((-x_1 + x_2) ** 2 + (-y_1 + y_2) ** 2) |
| 62 | + |
| 63 | + |
| 64 | +def quadratic_formula(a, b, c): |
| 65 | + r"""Quadratic Formula: \frac{-b + \sqrt{b^2 - 4ac}}{2a}""" |
| 66 | + return (-b + np.sqrt(b ** 2 + -4 * a * c)) / (2 * a) |
| 67 | + |
| 68 | + |
| 69 | +def kinematics(u, a, t): |
| 70 | + r"""Kinematics: u \cdot t + \frac{1}{2} a \cdot t^2""" |
| 71 | + return 0.5 * a * t ** 2 + t * u |
| 72 | + |
| 73 | + |
| 74 | +# Benchmark suite |
| 75 | +def run_benchmarks(): |
| 76 | + """Run all benchmarks and display results""" |
| 77 | + print("=" * 80) |
| 78 | + print("Python/NumPy Performance Benchmarks") |
| 79 | + print("=" * 80) |
| 80 | + print() |
| 81 | + |
| 82 | + results = [] |
| 83 | + |
| 84 | + # Simple Power |
| 85 | + print(f"Running: Simple Power (10,000 iterations)") |
| 86 | + test_data_simple_power = {"x":3,"y":4,"z":5} |
| 87 | + time_simple_power = benchmark(simple_power, 10000, **test_data_simple_power) |
| 88 | + result_simple_power = simple_power(**test_data_simple_power) |
| 89 | + print(f" Time: {time_simple_power:.2f} ms") |
| 90 | + print(f" Result: {result_simple_power}") |
| 91 | + results.append({ |
| 92 | + 'name': 'Simple Power', |
| 93 | + 'iterations': 10000, |
| 94 | + 'time_ms': time_simple_power, |
| 95 | + 'time_per_op_us': (time_simple_power * 1000) / 10000, |
| 96 | + 'result': result_simple_power |
| 97 | + }) |
| 98 | + print() |
| 99 | + |
| 100 | + # Polynomial |
| 101 | + print(f"Running: Polynomial (10,000 iterations)") |
| 102 | + test_data_polynomial = {"x":2.5} |
| 103 | + time_polynomial = benchmark(polynomial, 10000, **test_data_polynomial) |
| 104 | + result_polynomial = polynomial(**test_data_polynomial) |
| 105 | + print(f" Time: {time_polynomial:.2f} ms") |
| 106 | + print(f" Result: {result_polynomial}") |
| 107 | + results.append({ |
| 108 | + 'name': 'Polynomial', |
| 109 | + 'iterations': 10000, |
| 110 | + 'time_ms': time_polynomial, |
| 111 | + 'time_per_op_us': (time_polynomial * 1000) / 10000, |
| 112 | + 'result': result_polynomial |
| 113 | + }) |
| 114 | + print() |
| 115 | + |
| 116 | + # Trigonometric |
| 117 | + print(f"Running: Trigonometric (10,000 iterations)") |
| 118 | + test_data_trigonometric = {"x":1,"y":2,"z":3} |
| 119 | + time_trigonometric = benchmark(trigonometric, 10000, **test_data_trigonometric) |
| 120 | + result_trigonometric = trigonometric(**test_data_trigonometric) |
| 121 | + print(f" Time: {time_trigonometric:.2f} ms") |
| 122 | + print(f" Result: {result_trigonometric}") |
| 123 | + results.append({ |
| 124 | + 'name': 'Trigonometric', |
| 125 | + 'iterations': 10000, |
| 126 | + 'time_ms': time_trigonometric, |
| 127 | + 'time_per_op_us': (time_trigonometric * 1000) / 10000, |
| 128 | + 'result': result_trigonometric |
| 129 | + }) |
| 130 | + print() |
| 131 | + |
| 132 | + # Nested Expression |
| 133 | + print(f"Running: Nested Expression (10,000 iterations)") |
| 134 | + test_data_nested_expression = {"x":5,"y":6,"z":7,"a":1,"b":2,"c":3} |
| 135 | + time_nested_expression = benchmark(nested_expression, 10000, **test_data_nested_expression) |
| 136 | + result_nested_expression = nested_expression(**test_data_nested_expression) |
| 137 | + print(f" Time: {time_nested_expression:.2f} ms") |
| 138 | + print(f" Result: {result_nested_expression}") |
| 139 | + results.append({ |
| 140 | + 'name': 'Nested Expression', |
| 141 | + 'iterations': 10000, |
| 142 | + 'time_ms': time_nested_expression, |
| 143 | + 'time_per_op_us': (time_nested_expression * 1000) / 10000, |
| 144 | + 'result': result_nested_expression |
| 145 | + }) |
| 146 | + print() |
| 147 | + |
| 148 | + # Large Expression (50 terms) |
| 149 | + print(f"Running: Large Expression (50 terms) (1,000 iterations)") |
| 150 | + test_data_large_expression__50_terms_ = {"x":1.1} |
| 151 | + time_large_expression__50_terms_ = benchmark(large_expression__50_terms_, 1000, **test_data_large_expression__50_terms_) |
| 152 | + result_large_expression__50_terms_ = large_expression__50_terms_(**test_data_large_expression__50_terms_) |
| 153 | + print(f" Time: {time_large_expression__50_terms_:.2f} ms") |
| 154 | + print(f" Result: {result_large_expression__50_terms_}") |
| 155 | + results.append({ |
| 156 | + 'name': 'Large Expression (50 terms)', |
| 157 | + 'iterations': 1000, |
| 158 | + 'time_ms': time_large_expression__50_terms_, |
| 159 | + 'time_per_op_us': (time_large_expression__50_terms_ * 1000) / 1000, |
| 160 | + 'result': result_large_expression__50_terms_ |
| 161 | + }) |
| 162 | + print() |
| 163 | + |
| 164 | + # Many Variables (20 vars) |
| 165 | + print(f"Running: Many Variables (20 vars) (10,000 iterations)") |
| 166 | + test_data_many_variables__20_vars_ = {"x_0":1,"x_1":2,"x_2":3,"x_3":4,"x_4":5,"x_5":6,"x_6":7,"x_7":8,"x_8":9,"x_9":10,"x_10":11,"x_11":12,"x_12":13,"x_13":14,"x_14":15,"x_15":16,"x_16":17,"x_17":18,"x_18":19,"x_19":20} |
| 167 | + time_many_variables__20_vars_ = benchmark(many_variables__20_vars_, 10000, **test_data_many_variables__20_vars_) |
| 168 | + result_many_variables__20_vars_ = many_variables__20_vars_(**test_data_many_variables__20_vars_) |
| 169 | + print(f" Time: {time_many_variables__20_vars_:.2f} ms") |
| 170 | + print(f" Result: {result_many_variables__20_vars_}") |
| 171 | + results.append({ |
| 172 | + 'name': 'Many Variables (20 vars)', |
| 173 | + 'iterations': 10000, |
| 174 | + 'time_ms': time_many_variables__20_vars_, |
| 175 | + 'time_per_op_us': (time_many_variables__20_vars_ * 1000) / 10000, |
| 176 | + 'result': result_many_variables__20_vars_ |
| 177 | + }) |
| 178 | + print() |
| 179 | + |
| 180 | + # Distance Formula |
| 181 | + print(f"Running: Distance Formula (10,000 iterations)") |
| 182 | + test_data_distance_formula = {"x_1":0,"y_1":0,"x_2":3,"y_2":4} |
| 183 | + time_distance_formula = benchmark(distance_formula, 10000, **test_data_distance_formula) |
| 184 | + result_distance_formula = distance_formula(**test_data_distance_formula) |
| 185 | + print(f" Time: {time_distance_formula:.2f} ms") |
| 186 | + print(f" Result: {result_distance_formula}") |
| 187 | + results.append({ |
| 188 | + 'name': 'Distance Formula', |
| 189 | + 'iterations': 10000, |
| 190 | + 'time_ms': time_distance_formula, |
| 191 | + 'time_per_op_us': (time_distance_formula * 1000) / 10000, |
| 192 | + 'result': result_distance_formula |
| 193 | + }) |
| 194 | + print() |
| 195 | + |
| 196 | + # Quadratic Formula |
| 197 | + print(f"Running: Quadratic Formula (10,000 iterations)") |
| 198 | + test_data_quadratic_formula = {"a":1,"b":-5,"c":6} |
| 199 | + time_quadratic_formula = benchmark(quadratic_formula, 10000, **test_data_quadratic_formula) |
| 200 | + result_quadratic_formula = quadratic_formula(**test_data_quadratic_formula) |
| 201 | + print(f" Time: {time_quadratic_formula:.2f} ms") |
| 202 | + print(f" Result: {result_quadratic_formula}") |
| 203 | + results.append({ |
| 204 | + 'name': 'Quadratic Formula', |
| 205 | + 'iterations': 10000, |
| 206 | + 'time_ms': time_quadratic_formula, |
| 207 | + 'time_per_op_us': (time_quadratic_formula * 1000) / 10000, |
| 208 | + 'result': result_quadratic_formula |
| 209 | + }) |
| 210 | + print() |
| 211 | + |
| 212 | + # Kinematics |
| 213 | + print(f"Running: Kinematics (10,000 iterations)") |
| 214 | + test_data_kinematics = {"u":10,"a":9.8,"t":2} |
| 215 | + time_kinematics = benchmark(kinematics, 10000, **test_data_kinematics) |
| 216 | + result_kinematics = kinematics(**test_data_kinematics) |
| 217 | + print(f" Time: {time_kinematics:.2f} ms") |
| 218 | + print(f" Result: {result_kinematics}") |
| 219 | + results.append({ |
| 220 | + 'name': 'Kinematics', |
| 221 | + 'iterations': 10000, |
| 222 | + 'time_ms': time_kinematics, |
| 223 | + 'time_per_op_us': (time_kinematics * 1000) / 10000, |
| 224 | + 'result': result_kinematics |
| 225 | + }) |
| 226 | + print() |
| 227 | + |
| 228 | + # Summary |
| 229 | + print("=" * 80) |
| 230 | + print("Summary") |
| 231 | + print("=" * 80) |
| 232 | + print() |
| 233 | + print(f"{'Benchmark':<30} {'Iterations':<12} {'Total (ms)':<12} {'Per Op (μs)':<12}") |
| 234 | + print("-" * 80) |
| 235 | + |
| 236 | + for r in results: |
| 237 | + print(f"{r['name']:<30} {r['iterations']:<12,} {r['time_ms']:<12.2f} {r['time_per_op_us']:<12.6f}") |
| 238 | + |
| 239 | + print() |
| 240 | + print("=" * 80) |
| 241 | + print("Comparison with JavaScript (from compile-performance.test.ts)") |
| 242 | + print("=" * 80) |
| 243 | + print() |
| 244 | + print("To compare with JavaScript performance:") |
| 245 | + print(" npm run test compute-engine/compile-performance") |
| 246 | + print() |
| 247 | + print("Expected results:") |
| 248 | + print(" - NumPy should be faster than JavaScript for vectorized operations") |
| 249 | + print(" - JavaScript may be faster for single evaluations (less overhead)") |
| 250 | + print(" - Both should be much faster than interpreted evaluation") |
| 251 | + print() |
| 252 | + |
| 253 | +if __name__ == '__main__': |
| 254 | + run_benchmarks() |
0 commit comments