Parameterized BenchmarkπŸ”—

This tutorial demonstrates how to create a parameterized benchmark using SimpleBench, run it, and generate a report.

Parameterized benchmarks allow you to run the same benchmark function multiple times with different input parameters.

The key components of a parameterized benchmark are:

  • Benchmark Function: A function decorated with @simplebench.benchmark that contains the code to be benchmarked. The function’s signature should explicitly define the parameters that will be varied. SimpleBench will automatically pass the values from the kwargs_variations to these parameters.

  • Parameters: Defined as arguments for the @simplebench.benchmark decorator, which specifies different values to be used as inputs to the benchmark function.

kwargs_variations

A dictionary where the keys are parameter names that match the arguments of the benchmarked function and values are lists of possible values for those parameters.

The benchmark will be executed for every combination of the provided parameter values.

Example

kwargs_variations={
    'arg1': [1, 2],
    'arg2': ['a', 'b']
}

will result in the following combinations being tested:

{'arg1': 1, 'arg2': 'a'}
{'arg1': 1, 'arg2': 'b'}
{'arg1': 2, 'arg2': 'a'}
{'arg1': 2, 'arg2': 'b'}
variation_cols

A dictionary where keys are parameter names and values are display names for reporting. Fields not included here will usually not display in a report. Fields specified here must be a subset of the keys in kwargs_variations.

Example

variation_cols={
    'arg1': 'Argument 1',
    'arg2': 'Argument 2'
}

This will result in the report displaying columns/fields labeled β€˜Argument 1’ and β€˜Argument 2’ corresponding to the values of β€˜arg1’ and β€˜arg2’ used in each benchmark run.

use_field_for_n

A string specifying a parameter to use as the β€˜N’ field in reports, which is often used to indicate input size for complexity analysis. This parameter should be one of the keys in kwargs_variations. It is optional; if not specified, the β€˜N’ field will default to the value β€˜1.0’.

This is useful when you want to analyze how the performance of the benchmarked function scales with different input sizes or configurations. It is not required that the field be defined in variation_cols to be used as the β€˜N’ field.

Minimal ExampleπŸ”—

The minimal code required to create and run a parameterized benchmark using SimpleBench is creating a script that defines a function to be benchmarked with @simplebench.benchmark, specifying parameters using the @simplebench.benchmark decorator, and that calls simplebench.main()

A minimal parameterized benchmark exampleπŸ”—
 1"""A basic parameterized benchmark."""
 2import simplebench
 3
 4
 5@simplebench.benchmark(kwargs_variations={'size': [1, 5, 10, 50, 100, 500, 1000]},
 6                       variation_cols={'size': 'Input Size'},
 7                       use_field_for_n='size')
 8def addition_benchmark(size: int) -> None:
 9    """A simple addition benchmark of Python's built-in sum function."""
10    sum(range(size))
11
12
13if __name__ == "__main__":
14    simplebench.main()

Save this code to a file, for example minimal_parameterized_benchmark.py, and then run it from your terminal:

Generate a rich table report for operations-per-second by running a parameterized benchmarkπŸ”—
 python minimal_parameterized_benchmark.py --rich-table.ops --progress

This will produce output similar to the following:

Expected Output from Running the Parameterized BenchmarkπŸ”—
                                                                         addition_benchmark                                                                         
                                                                       operations per second                                                                        
                                                                                                                                                                    
                                                   A simple addition benchmark of Python's built-in sum function.                                                   
┏━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━┓
┃            ┃        ┃            ┃        ┃ Elapsed ┃             ┃               ┃            ┃            ┃            ┃             ┃                ┃        ┃
┃ Input Size ┃   N    ┃ Iterations ┃ Rounds ┃ Seconds ┃ mean kOps/s ┃ median kOps/s ┃ min kOps/s ┃ max kOps/s ┃ 5th kOps/s ┃ 95th kOps/s ┃ std dev kOps/s ┃  rsd%  ┃
┑━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━┩
β”‚     1      β”‚    1.0 β”‚  1010264   β”‚      1 β”‚  0.22   β”‚   4710.00   β”‚    4810.00    β”‚     46.50  β”‚  12000.00  β”‚   4000.00  β”‚   5990.00   β”‚      483.00    β”‚ 10.30% β”‚
β”‚     5      β”‚    5.0 β”‚   792381   β”‚      1 β”‚  0.18   β”‚   4500.00   β”‚    4780.00    β”‚     40.10  β”‚  12000.00  β”‚   4000.00  β”‚   4810.00   β”‚      426.00    β”‚  9.48% β”‚
β”‚     10     β”‚   10.0 β”‚   626015   β”‚      1 β”‚  0.15   β”‚   4170.00   β”‚    4000.00    β”‚     26.40  β”‚   8000.00  β”‚   4000.00  β”‚   4810.00   β”‚      388.00    β”‚  9.30% β”‚
β”‚     50     β”‚   50.0 β”‚   481444   β”‚      1 β”‚  0.19   β”‚   2600.00   β”‚    2670.00    β”‚     42.20  β”‚   4000.00  β”‚   2400.00  β”‚   2670.00   β”‚      195.00    β”‚  7.51% β”‚
β”‚    100     β”‚  100.0 β”‚   358702   β”‚      1 β”‚  0.20   β”‚   1840.00   β”‚    1850.00    β”‚     48.10  β”‚   2400.00  β”‚   1720.00  β”‚   2000.00   β”‚      116.00    β”‚  6.28% β”‚
β”‚    500     β”‚  500.0 β”‚    38035   β”‚      1 β”‚  0.11   β”‚    340.00   β”‚     343.00    β”‚     47.70  β”‚    358.00  β”‚    333.00  β”‚    348.00   β”‚       20.90    β”‚  6.15% β”‚
β”‚    1000    β”‚ 1000.0 β”‚    13805   β”‚      1 β”‚  0.09   β”‚    147.00   β”‚     149.00    β”‚     11.50  β”‚    152.00  β”‚    144.00  β”‚    150.00   β”‚        8.38    β”‚  5.69% β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜

A detailed explanation of the output format and displayed statistics shown in this example report can be found in the Rich Table Report section of the documentation.

Note

The command shown above includes the --progress option to display a progress bar while the benchmark is running. This option is not required to run the benchmark, and can be omitted if you do not wish to see the progress bar. It is not included in the expected output file since it produces dynamic output that changes as the benchmark runs and is removed from the terminal once the benchmark completes.

Multi-dimensional ParametersπŸ”—

You can define multiple parameters for a benchmark function by defining multiple parameters for the kwargs_variations.

For example, to benchmark a function with two parameters, size and mode, you can define the benchmark as follows:

A multidimensional parameterized benchmark exampleπŸ”—
 1"""A multidimensional parameterized benchmark."""
 2import simplebench
 3
 4
 5@simplebench.benchmark(
 6        kwargs_variations={
 7            'size': [10, 100, 1000],
 8            'mode': ['fast', 'slow']
 9        },
10        variation_cols={
11            'size': 'Input Size',
12            'mode': 'Mode'
13        },
14        use_field_for_n='size')
15def my_benchmark(size: int, mode: str) -> None:
16    """A benchmark for summing a range of numbers that varies by size and mode."""
17    match mode:
18        case 'fast':
19            sum(range(size))
20        case 'slow':
21            total = 0
22            for i in range(size):
23                total += i
24
25
26if __name__ == "__main__":
27    simplebench.main()

Save this code to a file, for example multidimensional_parameterized_benchmark.py, and then run it from your terminal:

Generate a rich table report for operations-per-second by running a multidimensional parameterized benchmarkπŸ”—
 python multidimensional_parameterized_benchmark.py --rich-table.ops --progress

This will run the benchmark for all combinations of size and mode, resulting in six different benchmark runs (3 sizes x 2 modes). The report will include columns for both β€˜Input Size’ and β€˜Mode’, allowing you to analyze the performance across these different dimensions.

And you will see output similar to the following:

Expected Output from Running the Multidimensional Parameterized BenchmarkπŸ”—
                                                                               my_benchmark                                                                                
                                                                           operations per second                                                                           
                                                                                                                                                                           
                                                 A benchmark for summing a range of numbers that varies by size and mode.                                                  
┏━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━┓
┃            ┃      ┃        ┃            ┃        ┃ Elapsed ┃             ┃               ┃            ┃            ┃            ┃             ┃                ┃        ┃
┃ Input Size ┃ Mode ┃   N    ┃ Iterations ┃ Rounds ┃ Seconds ┃ mean kOps/s ┃ median kOps/s ┃ min kOps/s ┃ max kOps/s ┃ 5th kOps/s ┃ 95th kOps/s ┃ std dev kOps/s ┃  rsd%  ┃
┑━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━┩
β”‚     10     β”‚ fast β”‚   10.0 β”‚   927749   β”‚      1 β”‚  0.25   β”‚   3800.00   β”‚    4000.00    β”‚     28.70  β”‚   8000.00  β”‚   3420.00  β”‚   4000.00   β”‚      392.00    β”‚ 10.30% β”‚
β”‚     10     β”‚ slow β”‚   10.0 β”‚   728337   β”‚      1 β”‚  0.32   β”‚   2330.00   β”‚    2400.00    β”‚     35.00  β”‚   3440.00  β”‚   2180.00  β”‚   2670.00   β”‚      161.00    β”‚  6.92% β”‚
β”‚    100     β”‚ fast β”‚  100.0 β”‚   537489   β”‚      1 β”‚  0.31   β”‚   1740.00   β”‚    1720.00    β”‚     24.70  β”‚   2400.00  β”‚   1600.00  β”‚   1850.00   β”‚      116.00    β”‚  6.65% β”‚
β”‚    100     β”‚ slow β”‚  100.0 β”‚   139185   β”‚      1 β”‚  0.33   β”‚    425.00   β”‚     428.00    β”‚     26.20  β”‚    471.00  β”‚    414.00  β”‚    444.00   β”‚       25.50    β”‚  5.99% β”‚
β”‚    1000    β”‚ fast β”‚ 1000.0 β”‚    22429   β”‚      1 β”‚  0.16   β”‚    140.00   β”‚     141.00    β”‚     27.60  β”‚    149.00  β”‚    138.00  β”‚    146.00   β”‚        8.45    β”‚  6.03% β”‚
β”‚    1000    β”‚ slow β”‚ 1000.0 β”‚     7963   β”‚      1 β”‚  0.20   β”‚     40.50   β”‚      40.80    β”‚      9.17  β”‚     43.50  β”‚     39.10  β”‚     42.10   β”‚        2.20    β”‚  5.44% β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜

You can customize the parameter values and names as needed for your specific benchmarking scenarios.