Skip to content
Writing

PyTorch vs. JAX vs. TensorFlow: A Roofline Benchmark Across 5 Architectures

An empirical investigation comparing PyTorch 2.14, JAX 0.11, and TensorFlow across five model architectures and four batch sizes, measuring compiler speedups, kernel overheads, and hardware limits on Apple Silicon.

Published
updated
Read
13 min
On this page
  1. The Experimental Setup
  2. Architectures under test
  3. Framework variants & measurement protocol
  4. 1. Where Models Land on the Roofline Frontier
  5. 2. Latency Breakdown by Architecture
  6. 3. The Compilation Showdown
  7. tf.function is transformative (10× to 17× speedup)
  8. jax.jit provides surgical fusion and near-roofline efficiency
  9. torch.compile() on CPU: Diminishing returns & regressions
  10. 4. Roofline Efficiency Heatmap
  11. What is eating the headroom?
  12. 5. Scaling with Batch Size
  13. 6. Measurement Stability & Noise
  14. Architecture-by-Architecture Winners
  15. Practical Takeaways for ML Engineers
  16. Code and Reproducibility

When evaluating machine learning frameworks, standard benchmarks often reduce the comparison to a single toy workload—a giant matrix multiplication or a standard ResNet-50 training pass—and declare a universal winner. But runtime performance is rarely that simple. A framework’s actual execution speed depends on its memory allocator, how its compiler handles kernel fusion, whether it can eliminate Python dispatch overhead, and the arithmetic intensity of the model architecture itself.

Following the release of neural-cost (and our introductory overview of roofline modeling), we conducted an extensive cross-framework scientific benchmark—documented in full in the GitHub Benchmark Report—to test how modern ML runtimes perform across diverse network topologies. Rather than merely recording wall-clock time, we analyzed each workload through the lens of a Roofline model to determine how close to the hardware’s theoretical limits each runtime gets.

We evaluated five distinct model topologies across PyTorch 2.14, JAX 0.11, and TensorFlow, comparing eager execution against compiled graph modes (torch.compile, jax.jit, and tf.function) across batch sizes from 1 to 128.

Here is what the empirical data revealed.


The Experimental Setup

All benchmarks were executed on an Apple M3 processor with unified memory architecture. Before running any model evaluations, we characterized the system’s empirical limits using neural-cost’s built-in hardware detection and STREAM triad benchmark:

  • Target Device: Apple M3 (8-core CPU)
  • Peak FP32 Compute: 3.60 TFLOP/s
  • Theoretical Peak Bandwidth: 100 GB/s
  • Empirical Bandwidth (STREAM triad): 41.8 GB/s
  • Hardware Ridge Point: 36.0 FLOP/byte

The ridge point (36.0 FLOP/byte) is the critical threshold of the roofline model: workloads with an arithmetic intensity below 36 FLOPs per byte of memory traffic are fundamentally memory-bandwidth bound, while workloads above 36 FLOP/byte are theoretically compute bound.

Architectures under test

We implemented five representative model architectures with equivalent layer dimensions and weight counts across all three frameworks:

ArchitectureTopology & ParametersComputational Profile
Feedforward DNN784 → 128 → 128 → 10, ReLU + LayerNorm (~475k params)Memory-bound dense projections
CNNConv64 (3×3) → BN → MaxPool → Conv128 (3×3) → BN → GAP → Dense10Spatial convolutions & pooling
Vanilla RNN2-layer RNN, hidden=128, seq=32 (~267k params)Sequential recurrence & loop overhead
LSTM2-layer LSTM (4-gate cell), hidden=128, seq=32 (~1.1M params)Recurrent gating & elementwise ops
Transformer2-layer Encoder (MHA h=4, d=128d=128, FFN×4, seq=32) (~1.5M params)Multi-head self-attention & layer norms

Framework variants & measurement protocol

Each framework was tested in two modes:

  1. PyTorch 2.14: Eager baseline vs. torch.compile() using the Inductor CPU backend.
  2. JAX 0.11: Eager XLA baseline vs. jax.jit() with full ahead-of-time graph compilation.
  3. TensorFlow: Eager baseline vs. tf.function() graph mode.

To ensure statistical rigor, we executed the automated benchmark runner benchmarks/collect_data.py:

  • Warmup: 15 full iterations per configuration to allow all JITs, static compilation graphs, and memory allocators to stabilize completely.
  • Timed repeats: 40 timed iterations per configuration.
  • Metrics recorded: Median latency, standard deviation (σ\sigma), coefficient of variation (CV%), roofline efficiency (lower_bound / observed), and achieved GFLOP/s.
  • Batch sweep: batch sizes of 1, 8, 32 and 128.

1. Where Models Land on the Roofline Frontier

The roofline model plots operational intensity (FLOPs per byte of memory traffic) on the x-axis against computational throughput (GFLOP/s) on the y-axis. The ceiling represents the hardware’s ultimate capabilities: an inclined slope limited by memory bandwidth, followed by a flat plateau capped by peak compute.

Three roofline panels, one per framework. Every point sits far below the roof, between about 37 and 270 GFLOP/s. PyTorch and TensorFlow place LSTM and Transformer just right of the 36 FLOP/byte ridge; all of JAX's points sit left of it.Three roofline panels, one per framework. Every point sits far below the roof, between about 37 and 270 GFLOP/s. PyTorch and TensorFlow place LSTM and Transformer just right of the 36 FLOP/byte ridge; all of JAX's points sit left of it.
Figure 1 Where each optimized variant lands on the Apple M3 roofline at batch=32. One panel per framework; marker shape is the architecture.

A few critical patterns emerge immediately from the roofline distribution:

  1. Inference at small-to-medium batch sizes is overwhelmingly memory-bound. Feedforward DNNs (AI≈10.7AI \approx 10.7 FLOP/B) and CNNs (AI≈AI \approx 24–33 FLOP/B) sit entirely to the left of the 36 FLOP/byte ridge point. Their latency is dictated by how efficiently memory blocks are moved through CPU caches rather than raw arithmetic ALU throughput.
  2. Only parameter-reusing models cross the ridge point. As measured through PyTorch and TensorFlow, the 2-layer LSTM (AI≈46.5AI \approx 46.5 FLOP/B) and Transformer encoder (AI≈47.2AI \approx 47.2 FLOP/B) technically have enough arithmetic operations relative to parameter and activation tensor volume to enter the compute-bound regime. JAX’s points all sit left of the ridge — its LSTM lands at just 5.9 FLOP/B — because neural-cost’s JAX adapter counts traffic from jaxpr primitives rather than layer weights, so it sees more bytes moved for the same FLOPs. Compare JAX with the other two along the throughput axis, not the intensity axis.
  3. Hardware saturation remains low on CPU. Even the fastest run at batch=32 — PyTorch eager on the Transformer — reaches only ~330 GFLOP/s, under 10% of the 3.6 TFLOP/s roof. At smaller batch sizes and modest hidden dimensions (d=128d=128), matrix shapes are not large enough to fully populate SIMD vector registers or sustain optimal cache-tiling strategies.

2. Latency Breakdown by Architecture

Comparing median latency across architectures at batch=32 highlights the massive divergence in runtime efficiency between frameworks.

Grouped bars on a log scale. JAX jit is fastest on FF DNN, RNN, LSTM and Transformer. TensorFlow tf.function is fastest on CNN, where JAX jit is slowest at 15.2 ms. PyTorch compiled on the FF DNN has a long whisker out to 0.41 ms.Grouped bars on a log scale. JAX jit is fastest on FF DNN, RNN, LSTM and Transformer. TensorFlow tf.function is fastest on CNN, where JAX jit is slowest at 15.2 ms. PyTorch compiled on the FF DNN has a long whisker out to 0.41 ms.
Figure 2 Median latency of each optimized variant at batch=32, log scale. Whiskers reach the 95th percentile.
ArchitecturePyTorch (eager)PyTorch (compile)JAX (eager)JAX (jit)TF (eager)TF (tf.function)
FF DNN0.062 ms0.204 ms0.096 ms0.082 ms2.602 ms0.183 ms
CNN9.067 ms8.372 ms6.352 ms15.213 ms8.647 ms5.012 ms
RNN1.778 ms1.862 ms1.490 ms0.893 ms64.474 ms3.661 ms
LSTM4.831 ms4.880 ms4.616 ms2.643 ms50.418 ms4.830 ms
Transformer2.586 ms3.122 ms1.827 ms1.731 ms20.224 ms6.251 ms

Bold marks the fastest run in each row. On the FF DNN that’s PyTorch eager: at this size, torch.compile only adds overhead.

Notice the disparity in recurrent workloads. In eager mode, executing 32 recurrent timesteps in Python causes TensorFlow to spend over 64 milliseconds on a 2-layer RNN, whereas JAX executes the exact same forward pass in 1.49 milliseconds—a 43× gap before compilation is even introduced.


3. The Compilation Showdown

Each framework provides an optimization pipeline intended to trace operations, fuse kernels, and avoid runtime dispatch overhead. Figure 3 plots the speedup ratio (eager latency ÷ optimized latency) across all five architectures.

Grouped bars on a log scale. TensorFlow tf.function gives 14.2× on FF DNN, 17.6× on RNN and 10.4× on LSTM. JAX jit gives 1.06–1.75× except on CNN, where it drops to 0.42×. torch.compile stays near 1× and falls to 0.31× on FF DNN.Grouped bars on a log scale. TensorFlow tf.function gives 14.2× on FF DNN, 17.6× on RNN and 10.4× on LSTM. JAX jit gives 1.06–1.75× except on CNN, where it drops to 0.42×. torch.compile stays near 1× and falls to 0.31× on FF DNN.
Figure 3 Speedup of each framework's compiled mode over its own eager mode at batch=32, log scale. Bars below the dashed 1× line got slower when compiled.

tf.function is transformative (10× to 17× speedup)

TensorFlow’s eager mode carries the heaviest per-op dispatch overhead among the three frameworks. Every single op invocation must cross the Python-C++ runtime boundary, query the device allocator, and queue an execution kernel. When a model executes many small operations sequentially (like an unrolled RNN or a dense projection stack), eager dispatch dominates wall-clock time.

Wrapping the forward pass in @tf.function compiles the entire model into a single static TensorFlow graph:

  • RNN: Latency drops from 64.47 ms down to 3.66 ms (17.6× speedup).
  • FF DNN: Latency drops from 2.60 ms down to 0.18 ms (14.2× speedup).
  • LSTM: Latency drops from 50.42 ms down to 4.83 ms (10.4× speedup).

tf.function does not necessarily generate faster mathematical kernels; rather, it completely removes the suffocating Python dispatch bottleneck.

jax.jit provides surgical fusion and near-roofline efficiency

JAX approaches compilation differently: its eager mode already dispatches to optimized XLA primitives, so eager overhead is already modest. However, jax.jit still delivers substantial gains for sequential models:

  • RNN & LSTM: Delivers 1.67× and 1.75× speedups at batch=32, bringing LSTM latency down to 2.64 ms (nearly 2× faster than either PyTorch or TensorFlow).

The standout result of the entire benchmark occurs at batch=1: jax.jit on LSTM reaches 99.5% roofline efficiency (0.18 ms observed vs. 0.18 ms theoretical bound). By compiling the timestep loop into a flat, statically scheduled XLA graph with zero dynamic memory allocation, JAX completely saturates the theoretical capability of the hardware.

torch.compile() on CPU: Diminishing returns & regressions

One of the most surprising findings was the behavior of torch.compile() on CPU:

  • On CNN, torch.compile() provided a modest 1.08× speedup (9.07 ms → 8.37 ms).
  • On recurrent and feedforward models at batch=32, it yielded flat or regressed performance: 0.95× on RNN, 0.99× on LSTM, 0.83× on Transformer, and 0.31× on FF DNN.

Why did PyTorch compile regress on CPU at small batch sizes? PyTorch eager CPU is already backed by decades of hand-tuned, assembly-optimized C++ BLAS libraries (MKL-DNN / OpenBLAS). When torch.compile invokes its default Inductor backend on CPU, it generates custom C++ OpenMP kernels. At small batch sizes and small hidden dimensions (N=128N=128), the overhead of thread pool synchronization and less aggressive loop vectorization in generated C++ can be slower than vendor-tuned GEMM kernels.

Inductor was primarily designed for GPUs, where kernel fusion eliminates massive VRAM round-trips and tensor cores require specific tiling patterns. On CPU at small batch sizes, PyTorch eager is remarkably difficult to beat.


4. Roofline Efficiency Heatmap

Roofline efficiency measures what percentage of the theoretical performance ceiling was achieved by the runtime:

Efficiency=Lower Bound LatencyObserved Latency=max⁡(FLOPsPeak FLOP/s,BytesBandwidth)Observed Runtime\text{Efficiency} = \frac{\text{Lower Bound Latency}}{\text{Observed Latency}} = \frac{\max\left(\frac{\text{FLOPs}}{\text{Peak FLOP/s}}, \frac{\text{Bytes}}{\text{Bandwidth}}\right)}{\text{Observed Runtime}}
Heatmap of roofline efficiency. JAX jit leads on FF DNN (8.6%), RNN (10.0%), LSTM (17.5%) and Transformer (11.5%); TensorFlow leads on CNN (11.0%). Most other cells are under 5%.Heatmap of roofline efficiency. JAX jit leads on FF DNN (8.6%), RNN (10.0%), LSTM (17.5%) and Transformer (11.5%); TensorFlow leads on CNN (11.0%). Most other cells are under 5%.
Figure 4 Roofline efficiency of each optimized variant at batch=32. The 0–30% colour scale matches the GPU post's heatmap, so the two can be compared directly.

Looking at the efficiency matrix:

  • JAX JIT leads in efficiency on four of five architectures, achieving 17.5% on LSTM, 11.5% on Transformer, and 10.0% on RNN.
  • TensorFlow eager is deeply inefficient for sequential models (0.2% on RNN and 0.3% on LSTM), but tf.function restores it to 3.1%–11.0%.
  • CNN efficiency peaks under TensorFlow (11.0%), because TensorFlow’s underlying CPU Conv2D implementation is exceptionally well-tuned for unified memory layouts.

What is eating the headroom?

Even for the best-performing compiled variants, efficiency numbers rarely exceed 15–20% on CPU. The neural-cost gap analysis identifies four primary sources of overhead:

  1. Launch & dispatch latency: Fixed overhead per kernel invocation that cannot be amortized when total execution time is under a millisecond.
  2. Untiled inner dimensions: Matrix multiplications with K=128K=128 fall below typical cache-blocking and register-tiling thresholds designed for matrices with dimensions in the thousands.
  3. Activation buffer allocations: Allocating intermediate workspaces between layers incurs page-table and memory management costs.
  4. Recurrent dependency chains: Each timestep requires serialized write-back of hidden state vectors before the next step can proceed.

5. Scaling with Batch Size

We swept batch sizes of 1, 8, 32 and 128 to observe how memory pressure and runtime dispatch behave under scaling.

Five log–log panels of latency against batch size. Latency rises with batch size in every panel, and JAX jit is lowest at batch 1 everywhere except CNN. The PyTorch compiled FF DNN line spikes at batch 32, the run with a 354% coefficient of variation.Five log–log panels of latency against batch size. Latency rises with batch size in every panel, and JAX jit is lowest at batch 1 everywhere except CNN. The PyTorch compiled FF DNN line spikes at batch 32, the run with a 354% coefficient of variation.
Figure 5 Median latency against batch size for each optimized variant, log–log. One panel per architecture.

Three observations stand out:

  1. Roughly proportional growth at larger batches: Between batch 32 and 128 (4× the work), latency rises 1.6–4.7× (the noisy FF DNN point aside). Because these workloads operate predominantly in the memory-bound regime, more samples means proportionally more tensor traffic through the memory bus.
  2. The CNN falls off a cliff between 8 and 32: PyTorch and JAX CNN latency jumps 9–14× for 4× more data, which suggests the activations stop fitting in cache. TensorFlow’s CNN rises only 3.4× over the same step.
  3. One point is noise, not a trend: PyTorch’s compiled FF DNN gets faster from batch 32 to 128 (0.204 → 0.085 ms). The batch-32 run is the one with a 354% coefficient of variation (Figure 6), so don’t read anything into that dip.

The figure plots optimized variants only, but the full results show the eager penalty shrinking with batch size too. At batch=1, dispatch overhead masks raw execution time: JAX’s eager LSTM is 22× slower than its jit version. By batch=128 that gap is 1.6×. TensorFlow is the exception — eager still runs 1.5–9× slower than tf.function at batch=128.


6. Measurement Stability & Noise

Benchmarking ML runtimes requires measuring not just speed, but reproducibility. We tracked the Coefficient of Variation (CV=σμ×100%CV = \frac{\sigma}{\mu} \times 100\%) across all 40 iterations.

Heatmap of coefficient of variation on a log colour scale. Most cells are between 1.5% and 4%. PyTorch compiled stands out at 354% on FF DNN, 22% on RNN and 10% on Transformer.Heatmap of coefficient of variation on a log colour scale. Most cells are between 1.5% and 4%. PyTorch compiled stands out at 354% on FF DNN, 22% on RNN and 10% on Transformer.
Figure 6 Run-to-run noise (coefficient of variation over 40 timed runs) for each optimized variant at batch=32. Log colour scale, so the 354% outlier doesn't wash out the rest.

The stability findings are unambiguous:

  • JAX JIT is consistently quiet (CVCV 1.5–3.7%): Ahead-of-time compilation produces the same execution trace every iteration, without dynamic garbage collection pauses or thread pool thrashing.
  • tf.function keeps TensorFlow steady (CVCV 1.7–3.9%). Eager TensorFlow wasn’t much noisier on average (1.1% on RNN, 8.0% on LSTM) — its problem is speed, not variance.
  • PyTorch compiled is the noisy one. Its CVCV hits 354% on the FF DNN, 22.2% on the RNN and 9.9% on the Transformer, most likely from thread scheduling variability in the Inductor CPU runtime. A CVCV that high means a few very slow iterations: the FF DNN’s median is 0.204 ms but its mean is 0.777 ms. The median is still usable; the mean isn’t.

Architecture-by-Architecture Winners

Summing up the optimized variants at batch=32 (detailed in the full results table):

ArchitectureFastest FrameworkLatencyEfficiencyNotes
FF DNNJAX (jit)0.082 ms8.6%2.2× faster than TF, 2.5× faster than compiled PyTorch. Eager PyTorch (0.062 ms) beats all three
CNNTensorFlow (tf.function)5.012 ms11.0%TF’s CPU Conv2D kernel outperforms PyTorch (8.37 ms) and JAX (15.21 ms)
RNNJAX (jit)0.893 ms10.0%2.1× faster than PyTorch (1.86 ms), 4.1× faster than TF (3.66 ms)
LSTMJAX (jit)2.643 ms17.5%1.8× faster than PyTorch (4.88 ms) and TF (4.83 ms)
TransformerJAX (jit)1.731 ms11.5%1.8× faster than PyTorch (3.12 ms), 3.6× faster than TF (6.25 ms)

JAX won 4 out of the 5 architectures among optimized variants, primarily due to XLA’s ability to fuse sequential elementwise operations and eliminate Python dispatch overhead. TensorFlow took the crown in CNN, showing the maturity of its CPU convolution implementations. One caveat: counting eager runs too, PyTorch eager takes the FF DNN.


Practical Takeaways for ML Engineers

  1. Compilation pays off when framework overhead dominates, not necessarily when BLAS does. tf.function gave massive 14×–17× speedups not by improving matrix multiply kernels, but by bypassing Python loop dispatch. If your model has loops or fine-grained operations, compilation is mandatory.
  2. Don’t assume torch.compile is a free lunch on CPU. On GPUs with large batch sizes, torch.compile is indispensable. But on CPU for small-batch inference, PyTorch’s eager C++ kernels are often faster and more predictable than Inductor codegen. Always measure before deploying.
  3. JAX remains the gold standard for predictable, small-to-medium inference. If you need minimal latency, rock-solid execution determinism (CVCV under 4% on every architecture here), and near-roofline efficiency on recurrent structures, jax.jit is exceptionally hard to beat.
  4. Always know your arithmetic intensity. If your model’s AIAI is under 36 FLOP/byte, optimizing FLOPs will not make it faster. Your performance bottleneck is memory traffic, tensor allocation, and cache reuse.

Code and Reproducibility

All benchmarks, automated figure generators, and full tabular data are open-source and reproducible on GitHub:


Updated October 19, 2026: corrected the fastest FF DNN result (eager PyTorch, not JAX), the RNN and Transformer speedup ratios in the winners table, and the measurement-noise figures, which now match the heatmap. The charts were regenerated from the same raw data.

Let's connect

Spot something I got wrong?

Corrections, follow-up questions, or a war story of your own. I read every message and reply to the ones that aren't a template.

Email me

Usually replies within a day or two · St. Louis, Missouri

ESC

Home

Main landing page & highlights

Page

Work

All engineering projects & case studies

Page

Writing

Technical notes & engineering articles

Page

Games

Browser games, several against real models

Page

About

Background, career timeline & skills

Page

Contact

Email, inquiries & consultation

Page

Copy Email Address

davidgraymi@gmail.com

Action

Toggle Light / Dark Theme

Switch color theme

Action

Resume

Web page, with a PDF download

Read

Visit GitHub

github.com/davidgraymi

External

Visit LinkedIn

linkedin.com/in/david-gray-mi

External

Beat the bot

Tetris against the value network from my reinforcement-learning project. Same pieces, two minutes, most lines wins.

Game

Draw a digit

Draw 0–9 and watch a weighted KNN classifier find its nearest MNIST neighbours as you go.

Game

Is it poisonous?

A real mushroom from the UCI dataset. Make your call, then watch an ID3 decision tree make its own.

Game

Route the robots

Draw a warehouse, place robots and goals, and race a LaCAM multi-agent path-finding solver to the shortest plan.

Game

Daily Wordlet

One five-letter word a day, the same for everyone. Six guesses.

Game

Bot or human?

14 rounds. One line from a public-domain classic, one written by a language model. Spot the bot.

Game

Reaction test

Wait for the panel to light up amber, then hit it as fast as you can.

Game

Lost signal

The 404 page's dodge game. Packets rain down; steer the receiver out of the way.

Game

neural-cost

A framework-neutral Python library that tells you how fast a neural network should run, not just how fast it does — FLOPs, tensor traffic and a roofline model across PyTorch, JAX and TensorFlow.

Personal

Sign Language Interpretation System

Control any smart device with American Sign Language — hand tracking, a gesture classifier and grammar correction, wired together over a client-server link.

Capstone

Patient Care Portal Triage

An NLP pipeline that routes patient portal messages to the right team and surfaces relevant evidence-based research while they wait for a human.

Research

Threat modeling a zero-trust deployment pipeline

A continuous deployment pipeline for configuration-as-code and infrastructure-as-code into Kubernetes, designed from its threat model outward so the release process does the security review.

Professional

Moving three toolchain apps to Kubernetes on Azure

Three toolchain applications moved to Kubernetes on Azure, deployed one way with Helm and infrastructure-as-code, and secured with an Istio service mesh.

Professional

A processor built from the ground up

Kernel, task scheduler and communication protocol written from nothing, in C and Ada, now in production and distributed globally.

Professional

A system library that runs anywhere

One portable C++ library targeting any RTOS or bare-metal system, packaged with Docker, Bazel and GitLab CI — cutting porting work by 4×.

Professional

LaCam for Multi-Agent Path Finding

A from-scratch LaCam solver for CS440 — agents searching a space of joint configurations, each expanded by a constraint tree that rules out collisions and swaps.

School

Bindersnap

GitHub for everyone who doesn't use GitHub — document management, review lifecycle, team collaboration, search and an in-browser editor.

Personal

Wordlet

A word guessing game built in Flutter and compiled to the web — playable right here, no install, no account.

Personal

Reinforcement Learning applied to Tetris

An agent that learns to play Tetris — a genuinely fun excuse to get hands-on with reward shaping.

School

Handwritten digit recognition with weighted KNN

A KNN classifier for handwritten digits, plus a k-value optimizer that collapses repeated distance calculations into a single pass.

School

ID3 mushroom classification

A from-scratch ID3 decision tree that predicts whether a mushroom is edible or poisonous from its attributes.

School

Healthcare length-of-stay estimator

A data mining project predicting how long a patient will stay, from admission-time features.

School

This website

A static site built with Astro, hosted free on GitHub Pages, designed so writing a post costs one markdown file and nothing else.

Personal

An Unexpected Email, REPRO-SIGN, and 11 Citations on Google Scholar

Years after our college capstone, an out-of-the-blue email from European AI researchers wanting to reproduce our sign language paper sent me to Google Scholar, where we've been cited 11 times.

Post

Connecting the Dots: How a Sign Language Capstone Got Me Hooked on AI

Looking back at my senior capstone project at Missouri State — building a real-time American Sign Language smart home controller with MediaPipe, custom CNNs, and networking — and how it sparked my path in machine learning.

Post

GPU vs. CPU: Roofline Benchmarks on Apple Silicon MPS with neural-cost v0.2.0

The neural-cost v0.2.0 GPU benchmark moves from CPU roofline analysis to Apple Silicon's MPS GPU backend — measuring PyTorch and JAX across five architectures, four batch sizes, and both eager and compiled execution modes.

Post

PyTorch vs. JAX vs. TensorFlow: A Roofline Benchmark Across 5 Architectures

An empirical investigation comparing PyTorch 2.14, JAX 0.11, and TensorFlow across five model architectures and four batch sizes, measuring compiler speedups, kernel overheads, and hardware limits on Apple Silicon.

Post

neural-cost: Roofline Analysis Across PyTorch, JAX, and TensorFlow

A framework-neutral Python library that estimates a neural network's FLOPs and tensor traffic, measures its runtime, and uses a roofline model to tell you exactly where performance is being left on the table.

Post

I rebuilt this site so that publishing costs one file

The old site was a single 41,000-character index.html. Every change was archaeology. Here is what I replaced it with and why the constraint was maintenance, not design.

Post
↑ ↓ to navigate↵ to select
esc to close

Terminal