Skip to content

PyPy vs GraalPy: Python JIT Performance Comparison (2026 Benchmarks)

Should I switch to PyPy or GraalPy for better Python performance? That’s the question I explored after seeing impressive benchmark numbers for both JIT-compiled Python implementations.

Both promise dramatic speedups with zero code changes. But the benchmarks only tell part of the story.

The Quick Decision

Decision Matrix
| Factor | Choose PyPy | Choose GraalPy |
|-----------------------|--------------------------------|---------------------------------|
| Python version | 3.11.11 | 3.12.8 |
| Max speedup | ~6x on n-body | 66x on spectral-norm |
| Ecosystem maturity | Battle-tested, 15+ years | Growing, Oracle-backed |
| Java integration | Not available | First-class support |
| Native binary | No | Yes (GraalVM native-image) |
| Platform support | x86, ARM, RISCV, PowerPC | Linux, macOS (Windows: exp.) |
| C extension support | Performance penalty | Near CPython, varies |
| JIT warmup time | Few seconds | Similar to PyPy |

If you need mature ecosystem support and broader platform compatibility, go with PyPy. If you need Java integration, polyglot capabilities, or compute-intensive numeric workloads, go with GraalPy.

What the Benchmarks Show

I compared both implementations against CPython using standard Benchmarks Game problems:

Performance Results
| Benchmark | CPython 3.14 | PyPy Speedup | GraalPy Speedup |
|---------------------|--------------|--------------|-----------------|
| spectral-norm | 1x | - | 66x |
| n-body | 1x | 6x | - |
| Python Perf Suite | 1x | ~4.8x avg | ~4x avg |

GraalPy’s 66x speedup on spectral-norm rivals compiled solutions like Cython and Rust. Both show 4-5x average speedup on standard benchmarks.

But here’s what the numbers don’t tell you.

The Compatibility Reality

The benchmarks represent best-case scenarios. Real-world adoption requires testing your entire dependency tree.

Compatibility Comparison
| Factor | PyPy | GraalPy |
|-------------------------|-------------------------------|-------------------------------|
| Python Version | 3.11.11 | 3.12.8 |
| CPython Test Pass Rate | Most stdlib | ~85% |
| PyPI Package Compat | Good (mature ecosystem) | 97% installable, 60%+ pass |
| C Extension Support | Significant perf penalty | Near CPython, varies |
| Native Extensions | Limited | Experimental (NumPy, PyTorch) |
| Windows Support | Production ready | Experimental |

A Reddit comment from the benchmark discussion captured this well:

“In practice, swapping to PyPy or GraalPy means testing your whole dependency tree, dealing with compatibility issues, and hoping the runtime keeps up with CPython releases.”

Understanding the Fundamental Difference

PyPy is Python with a tracing JIT compiler. It analyzes your running code and generates optimized machine code for hot paths.

PyPy JIT Behavior
+-------------------+
| Python bytecode |
+-------------------+
|
v
+-------------------+
| Interpreter runs |
| (collects traces) |
+-------------------+
|
v
+-------------------+
| JIT compiles |
| hot loops to |
| machine code |
+-------------------+

GraalPy is Python on the GraalVM. It leverages Oracle’s GraalVM JIT and offers polyglot capabilities.

GraalPy Architecture
+-------------------+
| Python code |
+-------------------+
|
v
+-------------------+
| GraalPy frontend |
| (Python -> Truffle AST) |
+-------------------+
|
v
+-------------------+
| GraalVM JIT |
| (optimizes AST) |
+-------------------+
|
v
+-------------------+
| Machine code |
+-------------------+

Both require warmup time. PyPy’s documentation suggests at least a few seconds of runtime for JIT optimization to take effect.

When PyPy Shines

1. Pure Python workloads. Long-running programs executing significant Python code benefit most from JIT compilation.

2. Server applications. Web servers, data processing pipelines where the JIT warmup time is amortized over long sessions.

3. Mature ecosystem needs. When you need stable, well-tested package compatibility.

4. Memory-hungry applications. PyPy can actually reduce memory footprint for large datasets compared to CPython.

Switching to PyPy
# Install via pyenv
pyenv install pypy3.11-7.3.17
pyenv shell pypy3.11-7.3.17
# Your existing Python code runs unchanged
python your_script.py

Avoid PyPy when:

  • Short-running scripts (JIT won’t warm up in under 0.2 seconds)
  • Heavy C extension usage (significant performance penalty)
  • Need latest Python version features (PyPy lags behind CPython)

When GraalPy Shines

1. Java integration. First-class embedding in Java applications with low-overhead polyglot calls.

pom.xml
<!-- Maven dependency for embedding GraalPy -->
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>polyglot</artifactId>
<version>25.0.2</version>
</dependency>
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>python</artifactId>
<version>25.0.2</version>
<type>pom</type>
</dependency>
JavaCallingPython.java
import org.graalvm.polyglot.*;
public class JavaCallingPython {
public static void main(String[] args) {
try (Context context = Context.create()) {
context.eval("python", "print('Hello from Python!')");
// Call Python function from Java
Value result = context.eval("python",
"def square(x): return x * x\n" +
"square(42)");
System.out.println(result.asInt()); // 1764
}
}
}

2. Polyglot applications. Mix Python with Java, JavaScript, Ruby, and other GraalVM languages in the same process.

3. Native binary deployment. Compile Python applications to standalone executables using GraalVM native-image.

4. Compute-intensive pure Python. Exceptional performance on numeric workloads.

Switching to GraalPy
# Install via pyenv
pyenv install graalpy-25.0.2
pyenv shell graalpy-25.0.2
# Your existing Python code runs unchanged
python your_script.py

Avoid GraalPy when:

  • Need Python 3.14 features (GraalPy is on 3.12)
  • Windows deployment (still experimental)
  • Heavy native extension usage (experimental support)

The Spectral-Norm Example

Here’s the benchmark code that showed GraalPy’s 66x speedup:

spectral_norm.py
# spectral-norm benchmark (from Benchmarks Game)
# This type of compute-heavy pure Python shows GraalPy's strength
def spectral_norm(n):
i = 1.0 / range(1, n + 1)
u = [1.0] * n
v = [0.0] * n
for _ in range(10):
v = [sum(u[j] / ((i + j) ** 0.5) for j in range(n)) for i in range(n)]
u = [sum(v[j] / ((i + j) ** 0.5) for j in range(n)) for i in range(n)]
return sum(u[i] ** 2 for i in range(n)) ** 0.5
# Results on same hardware:
# CPython 3.14: ~8.5 seconds
# GraalPy: ~0.13 seconds (66x speedup)

This is a best-case scenario: pure Python, compute-bound, no I/O, no C extensions.

Code Type Matters

Performance by Code Type
| Code Type | PyPy Benefit | GraalPy Benefit |
|---------------------|-----------------|------------------|
| Pure Python loops | 4-6x speedup | 4-66x speedup |
| C extension calls | Penalty | Near CPython |
| I/O bound | Minimal benefit | Minimal benefit |
| Short scripts | No benefit | No benefit |
| Memory-hungry | May reduce | Similar to CPython|

Both implementations struggle with the same categories:

  • Short-running scripts (no time for JIT warmup)
  • I/O bound workloads (JIT doesn’t help I/O)
  • Heavy C extension usage (compatibility and performance issues)

The Long-Term Maintenance Question

Alternative Python runtimes have a history of lagging behind CPython releases.

Version Lag
| Implementation | Current Version | CPython Latest | Lag |
|----------------|-----------------|----------------|------------|
| PyPy | 3.11.11 | 3.14 | ~2 years |
| GraalPy | 3.12.8 | 3.14 | ~1 year |

This matters when:

  • You need new language features (match statements, type hints, etc.)
  • Security patches need to flow through quickly
  • Your team expects python --version to match production

My Recommendation

The maintainer wisdom from the Reddit discussion is sound:

“Keep CPython as the orchestrator, drop into compiled extensions for the hot path.”

Choose PyPy if:

  • Your workload is pure Python with long runtime
  • You need mature ecosystem support
  • You want broader platform compatibility (ARM, RISCV, etc.)

Choose GraalPy if:

  • You need Java integration or polyglot capabilities
  • You want native binary compilation
  • Your workload is compute-intensive numeric code

Neither if:

  • You heavily rely on C extensions
  • You need bleeding-edge Python features
  • Your scripts are short-running

Decision Flowchart

Which JIT Runtime?
+---------------------+
| Is your code pure |
| Python with long |
| runtime? |
+---------------------+
|
+------------+------------+
| |
YES NO
| |
v v
+------------------+ +------------------+
| Need Java | | Consider Cython |
| integration? | | or Rust PyO3 |
+------------------+ +------------------+
|
+------+------+
| |
YES NO
| |
v v
+--------+ +------------+
|GraalPy | | Need mature|
| | | ecosystem? |
+--------+ +------------+
|
+------+------+
| |
YES NO
| |
v v
+-----+ +---------+
|PyPy | | GraalPy |
+-----+ +---------+

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments