Performance

Low-overhead Python calls for real Fortran workloads.

On the benchmark system, the normal PRIK interface delivered a 1.08× geometric-mean speedup over NumPy's f2py. Across 13 workloads, PRIK was faster in 10 and f2py in 2; 1 workload showed no statistically significant difference.

1.08× PRIK geometric-mean speedup
10 of 13 workloads faster with PRIK
1.28× best measured PRIK speedup

Relative performance of PRIK and f2py across 13 call, vector, and matrix workloads. Values above 1.0 mean PRIK is faster.

The chart shows f2py time ÷ PRIK time. Values to the right of 1.0× favor PRIK; values to the left favor f2py. Results close to 1.0× are practical parity and may move slightly between machines or runs.

Detailed Results

Lower times are better. Every row measures the same Fortran operation through the normal generated interface of each tool.

Workload f2py PRIK Relative result
Empty function call 34.6 ns 29.9 ns PRIK 1.16× faster
Add two scalars 340 ns 359 ns f2py 1.06× faster
Increment vector, 1 element 103 ns 80.5 ns PRIK 1.27× faster
Increment vector, 16 elements 106 ns 82.7 ns PRIK 1.28× faster
Increment vector, 1,024 elements 262 ns 210 ns PRIK 1.25× faster
Increment vector, 1,000,000 elements 186 µs 183 µs PRIK 1.02× faster
Sum 4×4 F-order matrix 127 ns 111 ns PRIK 1.15× faster
Sum 32×32 F-order matrix 718 ns 701 ns PRIK 1.02× faster
Sum 256×256 F-order matrix 38.9 µs 39.0 µs No significant difference
Sum 1,024×1,024 F-order matrix 620 µs 621 µs f2py 1.002× faster
Update 4×4 F-order matrix 247 ns 241 ns PRIK 1.03× faster
Update 256×256 F-order matrix 13.1 µs 12.8 µs PRIK 1.02× faster
Update 1,024×1,024 F-order matrix 198 µs 194 µs PRIK 1.02× faster
Geometric mean reference PRIK 1.08× faster

The smallest workloads expose wrapper overhead most clearly. As more time is spent inside Fortran, both tools approach the cost of the native operation and small differences matter less.

Clean Build Time

Build latency is measured separately from runtime-call overhead. Each timing starts with an empty output directory and ends when the normal tool command has generated its wrapper, compiled all native and generated sources, and linked an extension. Importability and the expected Python exports are checked immediately afterward, outside the timed interval.

The small-module workload uses the same one-source, five-procedure module as the runtime suite. The full-library workload gives both tools the same 155-source reference BLAS implementation and requires all 155 routines to be exposed. Each workload is built once as a development build with -O0 and once as an optimized build with -O3 -march=native -mtune=native.

Clean end-to-end build time for PRIK and f2py under development and optimized compiler profiles. Lower times are better.

Each value is the mean of 6 clean builds after 1 untimed warm-up.

Clean build workload f2py PRIK Relative result
Development (-O0) · small module (1 source, 5 procedures) 1.41 sec 494 ms PRIK 2.86× faster
Development (-O0) · full reference BLAS (155 sources) 6.85 sec 5.48 sec PRIK 1.25× faster
Optimized (-O3 -march=native -mtune=native) · small module (1 source, 5 procedures) 1.63 sec 590 ms PRIK 2.76× faster
Optimized (-O3 -march=native -mtune=native) · full reference BLAS (155 sources) 10.8 sec 13.6 sec f2py 1.26× faster

Fair, Like-for-Like Setup

The suite wraps one set of Fortran kernels with the default PRIK and f2py interfaces. It checks both extensions for the same results before measuring them. No benchmark-only wrapper mode is used.

  • Runtime native and generated sources use -O3 -march=native -mtune=native.
  • Clean builds use development (-O0) and optimized (-O3 -march=native -mtune=native) profiles.
  • Both interfaces keep the GIL held.
  • OpenMP, OpenBLAS, and MKL are limited to one thread.
  • pyperf --rigorous pins each benchmark to logical CPU 0.
  • PRIK build timings use up to 4 concurrent compiler processes; f2py uses its normal Meson/Ninja scheduler.
  • Build timings alternate tool order, use clean output directories, and exclude post-build import checks.
  • CPU: Arm Neoverse N2.
  • Operating system: Ubuntu 24.04.4 LTS.
  • Kernel/platform: Linux-6.17.0-1020-azure-aarch64-with-glibc2.39.
  • Python: 3.12.13.
  • NumPy/f2py: 2.5.1.
  • Fortran compiler: GNU Fortran 13.3.0.
  • pyperf: 2.10.0.
  • PRIK revision: 1787267cf673.

These results were recorded on August 3, 2026. Performance depends on the CPU, compiler, operating system, and background activity, so comparisons should use results produced together on the same machine.

Reproduce the Results

The complete benchmark suite is included in the repository. From the repository root, reproduce the build, correctness checks, measurements, and comparison with one command:

bash benchmarks/run.sh

The command writes the runtime and clean-build pyperf result pairs under benchmarks/results/ and prints both comparison tables.